Skip to content

Commit 926c6ed

Browse files
faernThomasdezeeuw
authored andcommitted
Don't assume memory layout of std::net::SocketAddr
1 parent b0f7784 commit 926c6ed

File tree

3 files changed

+161
-71
lines changed

3 files changed

+161
-71
lines changed

src/sockaddr.rs

+93-26
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
33
use std::{fmt, ptr};
44

55
use crate::sys::{
6-
c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
6+
sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET,
77
AF_INET6,
88
};
9+
#[cfg(windows)]
10+
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
911

1012
/// The address of a socket.
1113
///
@@ -24,7 +26,7 @@ impl SockAddr {
2426
/// It is up to the user to ensure the `addr` pointer and `len` length are
2527
/// correct.
2628
pub unsafe fn from_raw_parts(addr: *const sockaddr, len: socklen_t) -> SockAddr {
27-
let mut storage = MaybeUninit::<sockaddr_storage>::uninit();
29+
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
2830
ptr::copy_nonoverlapping(
2931
addr as *const _ as *const u8,
3032
storage.as_mut_ptr() as *mut u8,
@@ -33,7 +35,7 @@ impl SockAddr {
3335
SockAddr {
3436
// This is safe as we written the address to `storage` above.
3537
storage: storage.assume_init(),
36-
len: len,
38+
len,
3739
}
3840
}
3941

@@ -55,30 +57,50 @@ impl SockAddr {
5557
/// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IP v4)
5658
/// or `AF_INET6` (IP v6) family, otherwise returns `None`.
5759
pub fn as_std(&self) -> Option<SocketAddr> {
58-
self.as_inet()
59-
.map(|a| a.into())
60-
.or_else(|| self.as_inet6().map(|a| a.into()))
60+
if self.storage.ss_family == AF_INET as sa_family_t {
61+
// Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
62+
let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in) };
63+
64+
let ip = crate::sys::from_in_addr(addr.sin_addr);
65+
let port = u16::from_be(addr.sin_port);
66+
Some(SocketAddr::V4(SocketAddrV4::new(ip, port)))
67+
} else if self.storage.ss_family == AF_INET6 as sa_family_t {
68+
// Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
69+
let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in6) };
70+
71+
let ip = crate::sys::from_in6_addr(addr.sin6_addr);
72+
let port = u16::from_be(addr.sin6_port);
73+
Some(SocketAddr::V6(SocketAddrV6::new(
74+
ip,
75+
port,
76+
addr.sin6_flowinfo,
77+
#[cfg(unix)]
78+
addr.sin6_scope_id,
79+
#[cfg(windows)]
80+
unsafe {
81+
*addr.u.sin6_scope_id()
82+
},
83+
)))
84+
} else {
85+
None
86+
}
6187
}
6288

6389
/// Returns this address as a `SocketAddrV4` if it is in the `AF_INET`
6490
/// family.
6591
pub fn as_inet(&self) -> Option<SocketAddrV4> {
66-
if self.storage.ss_family as c_int == AF_INET {
67-
let storage: *const sockaddr_storage = (&self.storage) as *const _;
68-
Some(unsafe { *(storage as *const sockaddr_in as *const _) })
69-
} else {
70-
None
92+
match self.as_std() {
93+
Some(SocketAddr::V4(addr)) => Some(addr),
94+
_ => None,
7195
}
7296
}
7397

7498
/// Returns this address as a `SocketAddrV6` if it is in the `AF_INET6`
7599
/// family.
76100
pub fn as_inet6(&self) -> Option<SocketAddrV6> {
77-
if self.storage.ss_family as c_int == AF_INET6 {
78-
let storage: *const sockaddr_storage = (&self.storage) as *const _;
79-
Some(unsafe { *(storage as *const sockaddr_in6 as *const _) })
80-
} else {
81-
None
101+
match self.as_std() {
102+
Some(SocketAddr::V6(addr)) => Some(addr),
103+
_ => None,
82104
}
83105
}
84106
}
@@ -94,22 +116,67 @@ impl From<SocketAddr> for SockAddr {
94116

95117
impl From<SocketAddrV4> for SockAddr {
96118
fn from(addr: SocketAddrV4) -> SockAddr {
97-
unsafe {
98-
SockAddr::from_raw_parts(
99-
&addr as *const _ as *const _,
100-
mem::size_of::<SocketAddrV4>() as socklen_t,
101-
)
119+
let sockaddr_in = sockaddr_in {
120+
sin_family: AF_INET as sa_family_t,
121+
sin_port: addr.port().to_be(),
122+
sin_addr: crate::sys::to_in_addr(&addr.ip()),
123+
sin_zero: [0; 8],
124+
#[cfg(any(
125+
target_os = "dragonfly",
126+
target_os = "freebsd",
127+
target_os = "ios",
128+
target_os = "macos",
129+
target_os = "netbsd",
130+
target_os = "openbsd"
131+
))]
132+
sin_len: 0,
133+
};
134+
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
135+
// Safety: A `sockaddr_in` is memory compatible with a `sockaddr_storage`
136+
unsafe { (storage.as_mut_ptr() as *mut sockaddr_in).write(sockaddr_in) };
137+
SockAddr {
138+
storage: unsafe { storage.assume_init() },
139+
len: mem::size_of::<sockaddr_in>() as socklen_t,
102140
}
103141
}
104142
}
105143

106144
impl From<SocketAddrV6> for SockAddr {
107145
fn from(addr: SocketAddrV6) -> SockAddr {
108-
unsafe {
109-
SockAddr::from_raw_parts(
110-
&addr as *const _ as *const _,
111-
mem::size_of::<SocketAddrV6>() as socklen_t,
112-
)
146+
#[cfg(windows)]
147+
let u = unsafe {
148+
let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>();
149+
*u.sin6_scope_id_mut() = addr.scope_id();
150+
u
151+
};
152+
153+
let sockaddr_in6 = sockaddr_in6 {
154+
sin6_family: AF_INET6 as sa_family_t,
155+
sin6_port: addr.port().to_be(),
156+
sin6_addr: crate::sys::to_in6_addr(addr.ip()),
157+
sin6_flowinfo: addr.flowinfo(),
158+
#[cfg(unix)]
159+
sin6_scope_id: addr.scope_id(),
160+
#[cfg(windows)]
161+
u,
162+
#[cfg(any(
163+
target_os = "dragonfly",
164+
target_os = "freebsd",
165+
target_os = "ios",
166+
target_os = "macos",
167+
target_os = "netbsd",
168+
target_os = "openbsd"
169+
))]
170+
sin6_len: 0,
171+
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
172+
__sin6_src_id: 0,
173+
};
174+
let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
175+
// Safety: A `sockaddr_in6` is memory compatible with a `sockaddr_storage`
176+
unsafe { (storage.as_mut_ptr() as *mut sockaddr_in6).write(sockaddr_in6) };
177+
SockAddr {
178+
storage: unsafe { storage.assume_init() },
179+
len: mem::size_of::<sockaddr_in6>() as socklen_t,
113180
}
114181
}
115182
}

src/sys/unix.rs

+23-21
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
2323
use std::time::Duration;
2424
use std::{cmp, fmt, io, mem};
2525

26-
use libc::{self, c_void, ssize_t};
26+
use libc::{self, c_void, in6_addr, in_addr, ssize_t};
2727

2828
#[cfg(not(target_os = "redox"))]
2929
use crate::RecvFlags;
@@ -787,13 +787,12 @@ impl Socket {
787787
unsafe {
788788
let imr_interface: libc::in_addr =
789789
self.getsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF)?;
790-
Ok(from_s_addr(imr_interface.s_addr))
790+
Ok(from_in_addr(imr_interface))
791791
}
792792
}
793793

794794
pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> {
795-
let interface = to_s_addr(interface);
796-
let imr_interface = libc::in_addr { s_addr: interface };
795+
let imr_interface = to_in_addr(interface);
797796

798797
unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_MULTICAST_IF, imr_interface) }
799798
}
@@ -833,11 +832,9 @@ impl Socket {
833832
}
834833

835834
pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
836-
let multiaddr = to_s_addr(multiaddr);
837-
let interface = to_s_addr(interface);
838835
let mreq = libc::ip_mreq {
839-
imr_multiaddr: libc::in_addr { s_addr: multiaddr },
840-
imr_interface: libc::in_addr { s_addr: interface },
836+
imr_multiaddr: to_in_addr(multiaddr),
837+
imr_interface: to_in_addr(interface),
841838
};
842839
unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, mreq) }
843840
}
@@ -852,11 +849,9 @@ impl Socket {
852849
}
853850

854851
pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
855-
let multiaddr = to_s_addr(multiaddr);
856-
let interface = to_s_addr(interface);
857852
let mreq = libc::ip_mreq {
858-
imr_multiaddr: libc::in_addr { s_addr: multiaddr },
859-
imr_interface: libc::in_addr { s_addr: interface },
853+
imr_multiaddr: to_in_addr(multiaddr),
854+
imr_interface: to_in_addr(interface),
860855
};
861856
unsafe { self.setsockopt(libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, mreq) }
862857
}
@@ -1248,21 +1243,28 @@ fn timeval2dur(raw: libc::timeval) -> Option<Duration> {
12481243
}
12491244
}
12501245

1251-
fn to_s_addr(addr: &Ipv4Addr) -> libc::in_addr_t {
1252-
let octets = addr.octets();
1253-
u32::from_ne_bytes(octets)
1246+
pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> in_addr {
1247+
// `s_addr` is stored as BE on all machines, and the array is in BE order.
1248+
// So the native endian conversion method is used so that it's never swapped.
1249+
in_addr {
1250+
s_addr: u32::from_ne_bytes(addr.octets()),
1251+
}
12541252
}
12551253

1256-
fn from_s_addr(in_addr: libc::in_addr_t) -> Ipv4Addr {
1257-
in_addr.to_be().into()
1254+
pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr {
1255+
Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())
12581256
}
12591257

1260-
fn to_in6_addr(addr: &Ipv6Addr) -> libc::in6_addr {
1258+
pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> libc::in6_addr {
12611259
let mut ret: libc::in6_addr = unsafe { mem::zeroed() };
12621260
ret.s6_addr = addr.octets();
12631261
return ret;
12641262
}
12651263

1264+
pub(crate) fn from_in6_addr(in6_addr: in6_addr) -> Ipv6Addr {
1265+
Ipv6Addr::from(in6_addr.s6_addr)
1266+
}
1267+
12661268
#[cfg(target_os = "android")]
12671269
fn to_ipv6mr_interface(value: u32) -> c_int {
12681270
value as c_int
@@ -1297,12 +1299,12 @@ fn dur2linger(dur: Option<Duration>) -> libc::linger {
12971299
#[test]
12981300
fn test_ip() {
12991301
let ip = Ipv4Addr::new(127, 0, 0, 1);
1300-
assert_eq!(ip, from_s_addr(to_s_addr(&ip)));
1302+
assert_eq!(ip, from_in_addr(to_in_addr(&ip)));
13011303

13021304
let ip = Ipv4Addr::new(127, 34, 4, 12);
13031305
let want = 127 << 0 | 34 << 8 | 4 << 16 | 12 << 24;
1304-
assert_eq!(to_s_addr(&ip), want);
1305-
assert_eq!(from_s_addr(want), ip);
1306+
assert_eq!(to_in_addr(&ip).s_addr, want);
1307+
assert_eq!(from_in_addr(in_addr { s_addr: want }), ip);
13061308
}
13071309

13081310
#[test]

0 commit comments

Comments
 (0)