Skip to content

Commit 5d12687

Browse files
committed
Add SockAddrStorage type
1 parent 6828a5b commit 5d12687

File tree

4 files changed

+74
-32
lines changed

4 files changed

+74
-32
lines changed

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ compile_error!("Socket2 doesn't support the compile target");
185185

186186
use sys::c_int;
187187

188-
pub use sockaddr::{socklen_t, SockAddr};
188+
pub use sockaddr::{socklen_t, SockAddr, SockAddrStorage};
189189
pub use socket::Socket;
190190
pub use sockref::SockRef;
191191

src/sockaddr.rs

+62-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::hash::Hash;
2-
use std::mem::{self, size_of, MaybeUninit};
2+
use std::mem::{self, size_of};
33
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
44
use std::path::Path;
55
use std::{fmt, io, ptr};
@@ -17,6 +17,54 @@ use crate::Domain;
1717
#[allow(non_camel_case_types)]
1818
pub type socklen_t = crate::sys::socklen_t;
1919

20+
/// Rust version of the [`sockaddr_storage`] type.
21+
///
22+
/// This type is intended to be used with with direct calls to the `getsockname` syscall. See the
23+
/// documentation of [`SockAddr::new`] for examples.
24+
///
25+
/// This crate defines its own `sockaddr_storage` type to avoid semver concerns with upgrading
26+
/// `windows-sys`.
27+
#[repr(transparent)]
28+
pub struct SockAddrStorage {
29+
storage: sockaddr_storage,
30+
}
31+
32+
impl SockAddrStorage {
33+
/// Construct a new storage containing all zeros.
34+
#[inline]
35+
pub fn zeroed() -> Self {
36+
// SAFETY: All zeros is valid for this type.
37+
unsafe { mem::zeroed() }
38+
}
39+
40+
/// Returns the size of this storage.
41+
#[inline]
42+
pub fn size_of(&self) -> socklen_t {
43+
size_of::<Self>() as socklen_t
44+
}
45+
46+
/// View this type as another type.
47+
///
48+
/// # Safety
49+
///
50+
/// The type `T` must be one of the `sockaddr_*` types defined by this platform.
51+
#[inline]
52+
pub unsafe fn view_as<T>(&mut self) -> &mut T {
53+
assert!(size_of::<T>() <= size_of::<Self>());
54+
// SAFETY: This type is repr(transparent) over `sockaddr_storage` and `T` is one of the
55+
// `sockaddr_*` types defined by this platform.
56+
unsafe { &mut *(self as *mut Self as *mut T) }
57+
}
58+
}
59+
60+
impl std::fmt::Debug for SockAddrStorage {
61+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62+
f.debug_struct("sockaddr_storage")
63+
.field("ss_family", &self.storage.ss_family)
64+
.finish_non_exhaustive()
65+
}
66+
}
67+
2068
/// The address of a socket.
2169
///
2270
/// `SockAddr`s may be constructed directly to and from the standard library
@@ -44,23 +92,22 @@ impl SockAddr {
4492
/// # fn main() -> std::io::Result<()> {
4593
/// # #[cfg(unix)] {
4694
/// use std::io;
47-
/// use std::mem;
4895
/// use std::os::unix::io::AsRawFd;
4996
///
50-
/// use socket2::{SockAddr, Socket, Domain, Type};
97+
/// use socket2::{SockAddr, SockAddrStorage, Socket, Domain, Type};
5198
///
5299
/// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
53100
///
54101
/// // Initialise a `SocketAddr` byte calling `getsockname(2)`.
55-
/// let mut addr_storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
56-
/// let mut len = mem::size_of_val(&addr_storage) as libc::socklen_t;
102+
/// let mut addr_storage = SockAddrStorage::zeroed();
103+
/// let mut len = addr_storage.size_of();
57104
///
58105
/// // The `getsockname(2)` system call will intiliase `storage` for
59106
/// // us, setting `len` to the correct length.
60107
/// let res = unsafe {
61108
/// libc::getsockname(
62109
/// socket.as_raw_fd(),
63-
/// (&mut addr_storage as *mut libc::sockaddr_storage).cast(),
110+
/// addr_storage.view_as(),
64111
/// &mut len,
65112
/// )
66113
/// };
@@ -74,8 +121,11 @@ impl SockAddr {
74121
/// # Ok(())
75122
/// # }
76123
/// ```
77-
pub const unsafe fn new(storage: sockaddr_storage, len: socklen_t) -> SockAddr {
78-
SockAddr { storage, len }
124+
pub const unsafe fn new(storage: SockAddrStorage, len: socklen_t) -> SockAddr {
125+
SockAddr {
126+
storage: storage.storage,
127+
len: len as socklen_t,
128+
}
79129
}
80130

81131
/// Initialise a `SockAddr` by calling the function `init`.
@@ -125,25 +175,19 @@ impl SockAddr {
125175
/// ```
126176
pub unsafe fn try_init<F, T>(init: F) -> io::Result<(T, SockAddr)>
127177
where
128-
F: FnOnce(*mut sockaddr_storage, *mut socklen_t) -> io::Result<T>,
178+
F: FnOnce(*mut SockAddrStorage, *mut socklen_t) -> io::Result<T>,
129179
{
130180
const STORAGE_SIZE: socklen_t = size_of::<sockaddr_storage>() as socklen_t;
131181
// NOTE: `SockAddr::unix` depends on the storage being zeroed before
132182
// calling `init`.
133183
// NOTE: calling `recvfrom` with an empty buffer also depends on the
134184
// storage being zeroed before calling `init` as the OS might not
135185
// initialise it.
136-
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
186+
let mut storage = SockAddrStorage::zeroed();
137187
let mut len = STORAGE_SIZE;
138-
init(storage.as_mut_ptr(), &mut len).map(|res| {
188+
init(&mut storage, &mut len).map(|res| {
139189
debug_assert!(len <= STORAGE_SIZE, "overflown address storage");
140-
let addr = SockAddr {
141-
// Safety: zeroed-out `sockaddr_storage` is valid, caller must
142-
// ensure at least `len` bytes are valid.
143-
storage: storage.assume_init(),
144-
len,
145-
};
146-
(res, addr)
190+
(res, SockAddr::new(storage, len))
147191
})
148192
}
149193

src/sys/unix.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ use std::{io, slice};
7676
use libc::ssize_t;
7777
use libc::{in6_addr, in_addr};
7878

79-
use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
79+
use crate::{Domain, Protocol, SockAddr, SockAddrStorage, TcpKeepalive, Type};
8080
#[cfg(not(target_os = "redox"))]
8181
use crate::{MsgHdr, MsgHdrMut, RecvFlags};
8282

@@ -640,10 +640,10 @@ pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize {
640640

641641
#[allow(unsafe_op_in_unsafe_fn)]
642642
pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
643-
// SAFETY: a `sockaddr_storage` of all zeros is valid.
644-
let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
643+
let mut storage = SockAddrStorage::zeroed();
645644
let len = {
646-
let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<libc::sockaddr_un>() };
645+
// SAFETY: sockaddr_un is one of the sockaddr_* types defined by this platform.
646+
let storage = unsafe { storage.view_as::<libc::sockaddr_un>() };
647647

648648
let bytes = path.as_os_str().as_bytes();
649649
let too_long = match bytes.first() {
@@ -732,11 +732,10 @@ impl SockAddr {
732732
#[allow(unsafe_op_in_unsafe_fn)]
733733
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
734734
pub fn vsock(cid: u32, port: u32) -> SockAddr {
735-
// SAFETY: a `sockaddr_storage` of all zeros is valid.
736-
let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
735+
let mut storage = SockAddrStorage::zeroed();
737736
{
738-
let storage: &mut libc::sockaddr_vm =
739-
unsafe { &mut *((&mut storage as *mut sockaddr_storage).cast()) };
737+
// SAFETY: sockaddr_vm is one of the sockaddr_* types defined by this platform.
738+
let storage = unsafe { storage.view_as::<libc::sockaddr_vm>() };
740739
storage.svm_family = libc::AF_VSOCK as sa_family_t;
741740
storage.svm_cid = cid;
742741
storage.svm_port = port;

src/sys/windows.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use windows_sys::Win32::Networking::WinSock::{
3232
};
3333
use windows_sys::Win32::System::Threading::INFINITE;
3434

35-
use crate::{MsgHdr, RecvFlags, SockAddr, TcpKeepalive, Type};
35+
use crate::{MsgHdr, RecvFlags, SockAddr, SockAddrStorage, TcpKeepalive, Type};
3636

3737
#[allow(non_camel_case_types)]
3838
pub(crate) type c_int = std::os::raw::c_int;
@@ -900,11 +900,10 @@ pub(crate) fn original_dst_ipv6(socket: Socket) -> io::Result<SockAddr> {
900900

901901
#[allow(unsafe_op_in_unsafe_fn)]
902902
pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
903-
// SAFETY: a `sockaddr_storage` of all zeros is valid.
904-
let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
903+
let mut storage = SockAddrStorage::zeroed();
905904
let len = {
906-
let storage: &mut windows_sys::Win32::Networking::WinSock::SOCKADDR_UN =
907-
unsafe { &mut *(&mut storage as *mut sockaddr_storage).cast() };
905+
let storage =
906+
unsafe { storage.view_as::<windows_sys::Win32::Networking::WinSock::SOCKADDR_UN>() };
908907

909908
// Windows expects a UTF-8 path here even though Windows paths are
910909
// usually UCS-2 encoded. If Rust exposed OsStr's Wtf8 encoded

0 commit comments

Comments
 (0)