about summary refs log tree commit diff
path: root/library/std/src/sys/net
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/net')
-rw-r--r--library/std/src/sys/net/connection/socket/unix.rs9
-rw-r--r--library/std/src/sys/net/connection/socket/windows.rs80
-rw-r--r--library/std/src/sys/net/hostname/mod.rs14
-rw-r--r--library/std/src/sys/net/hostname/unix.rs62
-rw-r--r--library/std/src/sys/net/hostname/unsupported.rs6
-rw-r--r--library/std/src/sys/net/hostname/windows.rs24
-rw-r--r--library/std/src/sys/net/mod.rs3
7 files changed, 117 insertions, 81 deletions
diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs
index 8216f8d2fd5..a191576d93b 100644
--- a/library/std/src/sys/net/connection/socket/unix.rs
+++ b/library/std/src/sys/net/connection/socket/unix.rs
@@ -485,14 +485,15 @@ impl Socket {
 
     // bionic libc makes no use of this flag
     #[cfg(target_os = "linux")]
-    pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> {
-        setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int)
+    pub fn set_deferaccept(&self, accept: Duration) -> io::Result<()> {
+        let val = cmp::min(accept.as_secs(), c_int::MAX as u64) as c_int;
+        setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, val)
     }
 
     #[cfg(target_os = "linux")]
-    pub fn deferaccept(&self) -> io::Result<u32> {
+    pub fn deferaccept(&self) -> io::Result<Duration> {
         let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?;
-        Ok(raw as u32)
+        Ok(Duration::from_secs(raw as _))
     }
 
     #[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs
index b71d8b1357b..5b6f4cedf1b 100644
--- a/library/std/src/sys/net/connection/socket/windows.rs
+++ b/library/std/src/sys/net/connection/socket/windows.rs
@@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr};
 use crate::os::windows::io::{
     AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
 };
-use crate::sync::atomic::Atomic;
-use crate::sync::atomic::Ordering::{AcqRel, Relaxed};
 use crate::sys::c;
+use crate::sys::pal::winsock::last_error;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::time::Duration;
 use crate::{cmp, mem, ptr, sys};
@@ -112,84 +111,11 @@ pub(super) mod netc {
     }
 }
 
+pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init};
+
 #[expect(missing_debug_implementations)]
 pub struct Socket(OwnedSocket);
 
-static WSA_INITIALIZED: Atomic<bool> = Atomic::<bool>::new(false);
-
-/// Checks whether the Windows socket interface has been started already, and
-/// if not, starts it.
-#[inline]
-pub fn init() {
-    if !WSA_INITIALIZED.load(Relaxed) {
-        wsa_startup();
-    }
-}
-
-#[cold]
-fn wsa_startup() {
-    unsafe {
-        let mut data: c::WSADATA = mem::zeroed();
-        let ret = c::WSAStartup(
-            0x202, // version 2.2
-            &mut data,
-        );
-        assert_eq!(ret, 0);
-        if WSA_INITIALIZED.swap(true, AcqRel) {
-            // If another thread raced with us and called WSAStartup first then call
-            // WSACleanup so it's as though WSAStartup was only called once.
-            c::WSACleanup();
-        }
-    }
-}
-
-pub fn cleanup() {
-    // We don't need to call WSACleanup here because exiting the process will cause
-    // the OS to clean everything for us, which is faster than doing it manually.
-    // See #141799.
-}
-
-/// Returns the last error from the Windows socket interface.
-fn last_error() -> io::Error {
-    io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
-}
-
-#[doc(hidden)]
-pub trait IsMinusOne {
-    fn is_minus_one(&self) -> bool;
-}
-
-macro_rules! impl_is_minus_one {
-    ($($t:ident)*) => ($(impl IsMinusOne for $t {
-        fn is_minus_one(&self) -> bool {
-            *self == -1
-        }
-    })*)
-}
-
-impl_is_minus_one! { i8 i16 i32 i64 isize }
-
-/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
-/// and if so, returns the last error from the Windows socket interface. This
-/// function must be called before another call to the socket API is made.
-pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
-    if t.is_minus_one() { Err(last_error()) } else { Ok(t) }
-}
-
-/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
-pub fn cvt_gai(err: c_int) -> io::Result<()> {
-    if err == 0 { Ok(()) } else { Err(last_error()) }
-}
-
-/// Just to provide the same interface as sys/pal/unix/net.rs
-pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
-where
-    T: IsMinusOne,
-    F: FnMut() -> T,
-{
-    cvt(f())
-}
-
 impl Socket {
     pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
         let family = match *addr {
diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs
new file mode 100644
index 00000000000..8ffe4894d71
--- /dev/null
+++ b/library/std/src/sys/net/hostname/mod.rs
@@ -0,0 +1,14 @@
+cfg_select! {
+    all(target_family = "unix", not(target_os = "espidf")) => {
+        mod unix;
+        pub use unix::hostname;
+    }
+    target_os = "windows" => {
+        mod windows;
+        pub use windows::hostname;
+    }
+    _ => {
+        mod unsupported;
+        pub use unsupported::hostname;
+    }
+}
diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs
new file mode 100644
index 00000000000..bc6fa82a38f
--- /dev/null
+++ b/library/std/src/sys/net/hostname/unix.rs
@@ -0,0 +1,62 @@
+use crate::ffi::OsString;
+use crate::io;
+use crate::os::unix::ffi::OsStringExt;
+use crate::sys::pal::os::errno;
+
+pub fn hostname() -> io::Result<OsString> {
+    // Query the system for the maximum host name length.
+    let host_name_max = match unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } {
+        // If this fails (possibly because there is no maximum length), then
+        // assume a maximum length of _POSIX_HOST_NAME_MAX (255).
+        -1 => 255,
+        max => max as usize,
+    };
+
+    // Reserve space for the nul terminator too.
+    let mut buf = Vec::<u8>::try_with_capacity(host_name_max + 1)?;
+    loop {
+        // SAFETY: `buf.capacity()` bytes of `buf` are writable.
+        let r = unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.capacity()) };
+        match (r != 0).then(errno) {
+            None => {
+                // Unfortunately, the UNIX specification says that the name will
+                // be truncated if it does not fit in the buffer, without returning
+                // an error. As additionally, the truncated name may still be null-
+                // terminated, there is no reliable way to  detect truncation.
+                // Fortunately, most platforms ignore what the specification says
+                // and return an error (mostly ENAMETOOLONG). Should that not be
+                // the case, the following detects truncation if the null-terminator
+                // was omitted. Note that this check does not impact performance at
+                // all as we need to find the length of the string anyways.
+                //
+                // Use `strnlen` as it does not place an initialization requirement
+                // on the bytes after the nul terminator.
+                //
+                // SAFETY: `buf.capacity()` bytes of `buf` are accessible, and are
+                // initialized up to and including a possible nul terminator.
+                let len = unsafe { libc::strnlen(buf.as_ptr().cast(), buf.capacity()) };
+                if len < buf.capacity() {
+                    // If the string is nul-terminated, we assume that is has not
+                    // been truncated, as the capacity *should be* enough to hold
+                    // `HOST_NAME_MAX` bytes.
+                    // SAFETY: `len + 1` bytes have been initialized (we exclude
+                    // the nul terminator from the string).
+                    unsafe { buf.set_len(len) };
+                    return Ok(OsString::from_vec(buf));
+                }
+            }
+            // As `buf.capacity()` is always less than or equal to `isize::MAX`
+            // (Rust allocations cannot exceed that limit), the only way `EINVAL`
+            // can be returned is if the system uses `EINVAL` to report that the
+            // name does not fit in the provided buffer. In that case (or in the
+            // case of `ENAMETOOLONG`), resize the buffer and try again.
+            Some(libc::EINVAL | libc::ENAMETOOLONG) => {}
+            // Other error codes (e.g. EPERM) have nothing to do with the buffer
+            // size and should be returned to the user.
+            Some(err) => return Err(io::Error::from_raw_os_error(err)),
+        }
+
+        // Resize the buffer (according to `Vec`'s resizing rules) and try again.
+        buf.try_reserve(buf.capacity() + 1)?;
+    }
+}
diff --git a/library/std/src/sys/net/hostname/unsupported.rs b/library/std/src/sys/net/hostname/unsupported.rs
new file mode 100644
index 00000000000..d868f68f32d
--- /dev/null
+++ b/library/std/src/sys/net/hostname/unsupported.rs
@@ -0,0 +1,6 @@
+use crate::ffi::OsString;
+use crate::io::{Error, Result};
+
+pub fn hostname() -> Result<OsString> {
+    Err(Error::UNSUPPORTED_PLATFORM)
+}
diff --git a/library/std/src/sys/net/hostname/windows.rs b/library/std/src/sys/net/hostname/windows.rs
new file mode 100644
index 00000000000..24eed100f32
--- /dev/null
+++ b/library/std/src/sys/net/hostname/windows.rs
@@ -0,0 +1,24 @@
+use crate::ffi::OsString;
+use crate::io::Result;
+use crate::mem::MaybeUninit;
+use crate::os::windows::ffi::OsStringExt;
+use crate::sys::pal::c;
+use crate::sys::pal::winsock::{self, cvt};
+
+pub fn hostname() -> Result<OsString> {
+    winsock::startup();
+
+    // The documentation of GetHostNameW says that a buffer size of 256 is
+    // always enough.
+    let mut buffer = [const { MaybeUninit::<u16>::uninit() }; 256];
+    // SAFETY: these parameters specify a valid, writable region of memory.
+    cvt(unsafe { c::GetHostNameW(buffer.as_mut_ptr().cast(), buffer.len() as i32) })?;
+    // Use `lstrlenW` here as it does not require the bytes after the nul
+    // terminator to be initialized.
+    // SAFETY: if `GetHostNameW` returns successfully, the name is nul-terminated.
+    let len = unsafe { c::lstrlenW(buffer.as_ptr().cast()) };
+    // SAFETY: the length of the name is `len`, hence `len` bytes have been
+    //         initialized by `GetHostNameW`.
+    let name = unsafe { buffer[..len as usize].assume_init_ref() };
+    Ok(OsString::from_wide(name))
+}
diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs
index dffc4ea7f81..bfe5cf53128 100644
--- a/library/std/src/sys/net/mod.rs
+++ b/library/std/src/sys/net/mod.rs
@@ -2,3 +2,6 @@
 /// `UdpSocket` as well as related functionality like DNS resolving.
 mod connection;
 pub use connection::*;
+
+mod hostname;
+pub use hostname::hostname;