1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::{
cell::UnsafeCell,
io, mem,
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
ptr,
};
pub unsafe fn libc_socketaddr_into_std(
storage: *const libc::sockaddr_storage,
) -> io::Result<SocketAddr> {
// SAFETY: correct pointer.
let sockaddr = unsafe { *storage };
if sockaddr.ss_family == libc::AF_INET as libc::sa_family_t {
let ipv4_ptr = storage.cast::<libc::sockaddr_in>();
// SAFETY: We've verified ss_family is AF_INET, so the storage pointer can be safely
// cast to sockaddr_in. The caller guarantees storage points to valid memory.
let ipv4 = Ipv4Addr::from(unsafe { *ipv4_ptr }.sin_addr.s_addr.to_be());
// SAFETY: Same as above - pointer is valid and properly aligned for sockaddr_in.
let port = u16::from_be(unsafe { *ipv4_ptr }.sin_port);
Ok(SocketAddr::from(SocketAddrV4::new(ipv4, port)))
} else if sockaddr.ss_family == libc::AF_INET6 as libc::sa_family_t {
let ipv6_ptr = storage.cast::<libc::sockaddr_in6>();
// SAFETY: correct.
let in6 = unsafe { *ipv6_ptr };
let ipv6 =
Ipv6Addr::from(u128::from_le_bytes(in6.sin6_addr.s6_addr).to_be());
let port = u16::from_be(in6.sin6_port);
Ok(SocketAddr::from(SocketAddrV6::new(
ipv6,
port,
in6.sin6_flowinfo,
in6.sin6_scope_id,
)))
} else {
Err(io::Error::from_raw_os_error(libc::EAFNOSUPPORT))
}
}
pub fn std_socketaddr_into_libc(addr: SocketAddr) -> libc::sockaddr_storage {
// SAFETY: sockaddr_storage is a C struct designed to hold any socket address type.
// Zero-initialization is valid - all fields are primitive types where zero is safe.
let storage: UnsafeCell<libc::sockaddr_storage> =
UnsafeCell::new(unsafe { mem::zeroed() });
match addr {
// SAFETY: copy_nonoverlapping is safe because:
// 1. Source (&into_addr(v4)) is a valid, aligned sockaddr_in on the stack
// 2. Destination (storage.get()) is valid - we just created it
// 3. Size is correct (size_of::<sockaddr_in>())
// 4. Regions don't overlap (source is on stack, dest is in UnsafeCell)
// 5. sockaddr_in fits in sockaddr_storage by design
SocketAddr::V4(v4) => unsafe {
// We copy the bytes from the source pointer (&v4)
// to the destination pointer (&mut storage)
ptr::copy_nonoverlapping(
&into_addr(v4) as *const _ as *const u8,
storage.get() as *mut u8,
// We calculate the size of the IPv4 address structure
mem::size_of::<libc::sockaddr_in>(),
);
},
// SAFETY: copy_nonoverlapping is safe because:
// 1. Source (&into_addr6(v6)) is a valid, aligned sockaddr_in6 on the stack
// 2. Destination (storage.get()) is valid - we just created it
// 3. Size is correct (size_of::<sockaddr_in6>())
// 4. Regions don't overlap (source is on stack, dest is in UnsafeCell)
// 5. sockaddr_in6 fits in sockaddr_storage by design
SocketAddr::V6(v6) => unsafe {
// We copy the bytes from the source pointer (&v6)
// to the destination pointer (&mut storage)
ptr::copy_nonoverlapping(
&into_addr6(v6) as *const _ as *const u8,
storage.get() as *mut u8,
// We calculate the size of the IPv6 address structure
mem::size_of::<libc::sockaddr_in6>(),
);
},
};
storage.into_inner()
}
fn into_addr(addr: SocketAddrV4) -> libc::sockaddr_in {
// SAFETY: sockaddr_in is a C struct with primitive integer fields.
// Zero-initialization is safe - all fields accept zero as a valid value.
let mut _addr: libc::sockaddr_in = unsafe { mem::zeroed() };
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly"
))]
{
_addr.sin_len = mem::size_of::<libc::sockaddr_in>() as u8;
}
_addr.sin_family = libc::AF_INET as libc::sa_family_t;
_addr.sin_port = addr.port().to_be();
_addr.sin_addr = libc::in_addr { s_addr: u32::from(*addr.ip()).to_be() };
_addr
}
fn into_addr6(addr: SocketAddrV6) -> libc::sockaddr_in6 {
// SAFETY: sockaddr_in6 is a C struct with primitive integer/array fields.
// Zero-initialization is safe - all fields accept zero as a valid value.
let mut _addr: libc::sockaddr_in6 = unsafe { mem::zeroed() };
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly"
))]
{
_addr.sin6_len = mem::size_of::<libc::sockaddr_in6>() as u8;
}
_addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
_addr.sin6_port = addr.port().to_be();
_addr.sin6_addr = libc::in6_addr { s6_addr: addr.ip().octets() };
_addr
}