Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion library/std/src/net/hostname.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ use crate::ffi::OsString;
/// This can error out in platform-specific error cases;
/// for example, uefi and wasm, where hostnames aren't
/// supported.
///
/// # Underlying system calls
///
/// | Platform | System call |
/// |----------|---------------------------------------------------------------------------------------------------------|
/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) |
/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) |
///
/// Note that platform-specific behavior [may change in the future][changes].
///
/// [changes]: crate::io#platform-specific-behavior
#[unstable(feature = "gethostname", issue = "135142")]
pub fn hostname() -> crate::io::Result<OsString> {
crate::sys_common::net::gethostname()
crate::sys::net::gethostname()
}
31 changes: 30 additions & 1 deletion library/std/src/sys/pal/unix/net.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t};

use crate::ffi::CStr;
use crate::ffi::{CStr, OsString};
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::ffi::OsStringExt;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::pal::unix::IsMinusOne;
Expand Down Expand Up @@ -649,3 +650,31 @@ fn on_resolver_failure() {

#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
fn on_resolver_failure() {}

pub fn gethostname() -> io::Result<OsString> {
// 255 bytes is the maximum allowable length for a hostname (as per the DNS spec),
// so we shouldn't ever have problems with this. I (@orowith2os) considered using a constant
// and letting the platform set the length, but it was determined after some discussion that
// this could break things if the platform changes their length. Possible alternative is to
// read the sysconf setting for the max hostname length, but that might be a bit too much work.
// The 256 byte length is to allow for the NUL terminator.
let mut temp_buffer = [0; 256];

// SAFETY: we're passing in a valid (0-initialized) buffer, and the length of said buffer.
unsafe {
cvt(libc::gethostname(temp_buffer.as_mut_ptr() as _, temp_buffer.len() as _))?;
}

// Unfortunately, the UNIX specification says that the name will be truncated
// if it does not fit in the buffer, without returning. As additionally, the
// truncated name may still be null-terminated, there is no reliable way to
// detect truncation. Fortunately, most platforms ignore what the specication
// 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.
match CStr::from_bytes_until_nul(&temp_buffer) {
Ok(bytes) => Ok(OsString::from_vec(bytes.to_bytes().to_vec())),
Err(_) => Err(io::Error::from_raw_os_error(libc::ENAMETOOLONG)),
}
}
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/windows/c/bindings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1978,7 +1978,7 @@ Windows.Win32.Networking.WinSock.FD_SET
Windows.Win32.Networking.WinSock.FIONBIO
Windows.Win32.Networking.WinSock.freeaddrinfo
Windows.Win32.Networking.WinSock.getaddrinfo
Windows.Win32.Networking.WinSock.gethostname
Windows.Win32.Networking.WinSock.GetHostNameW
Windows.Win32.Networking.WinSock.getpeername
Windows.Win32.Networking.WinSock.getsockname
Windows.Win32.Networking.WinSock.getsockopt
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, e
windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL);
windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn WSACleanup() -> i32);
windows_targets::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR);
Expand All @@ -126,7 +127,6 @@ windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA));
windows_targets::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn gethostname(name : PSTR, namelen : i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32);
Expand Down
25 changes: 23 additions & 2 deletions library/std/src/sys/pal/windows/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

use core::ffi::{c_int, c_long, c_ulong, c_ushort};

use crate::ffi::OsString;
use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::net::{Shutdown, SocketAddr};
use crate::os::windows::ffi::OsStringExt;
use crate::os::windows::io::{
AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
};
Expand Down Expand Up @@ -31,8 +33,8 @@ pub mod netc {
IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6,
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST,
SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr,
SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, gethostname,
getpeername, getsockname, getsockopt, listen, setsockopt,
SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername,
getsockname, getsockopt, listen, setsockopt,
};

#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -572,3 +574,22 @@ impl FromRawSocket for Socket {
unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) }
}
}

pub fn gethostname() -> io::Result<OsString> {
init();

// The documentation of GetHostNameW says that a buffer size of 256 is
// always enough.
let mut buffer = [0; 256];
// SAFETY: these parameters specify a valid, writable region of memory.
let ret = unsafe { c::GetHostNameW(buffer.as_mut_ptr(), buffer.len() as i32) };
if ret == 0 {
let len = buffer
.iter()
.position(|&w| w == 0)
.expect("GetHostNameW failed to return a null-terminated name");
Ok(OsString::from_wide(&buffer[..len]))
} else {
Err(io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }))
}
}
27 changes: 1 addition & 26 deletions library/std/src/sys_common/net.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(test)]
mod tests;

use crate::ffi::{CStr, OsString, c_char, c_int, c_void};
use crate::ffi::{c_int, c_void};
use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::sys::common::small_c_string::run_with_cstr;
Expand Down Expand Up @@ -215,31 +215,6 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
}
}

////////////////////////////////////////////////////////////////////////////////
// gethostname
////////////////////////////////////////////////////////////////////////////////

pub fn gethostname() -> crate::io::Result<OsString> {
init();
// 255 bytes is the maximum allowable length for a hostname (as per the DNS spec),
// so we shouldn't ever have problems with this. I (@orowith2os) considered using a constant
// and letting the platform set the length, but it was determined after some discussion that
// this could break things if the platform changes their length. Possible alternative is to
// read the sysconf setting for the max hostname length, but that might be a bit too much work.
// The 256 byte length is to allow for the NUL terminator.
let mut temp_buffer: [c_char; 256] = [0; 256];

// SAFETY: should never be unsafe, as we're passing in a valid (0-initialized) buffer, and the
// length of said buffer.
unsafe {
cvt(c::gethostname(temp_buffer.as_mut_ptr() as _, temp_buffer.len() as _))?;
}

// SAFETY: we already know the pointer here is valid, we made it from safe Rust earlier.
let cstring = unsafe { CStr::from_ptr(temp_buffer.as_mut_ptr()) };
Ok(OsString::from(cstring.to_string_lossy().as_ref()))
}

////////////////////////////////////////////////////////////////////////////////
// TCP streams
////////////////////////////////////////////////////////////////////////////////
Expand Down