Skip to content

Commit 152e075

Browse files
authored
Don't assume memory layout of std::net::SocketAddr
Fixes #1388.
1 parent 13e82ce commit 152e075

File tree

6 files changed

+176
-46
lines changed

6 files changed

+176
-46
lines changed

src/sys/unix/net.rs

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use std::net::SocketAddr;
1+
use std::mem;
2+
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
23

34
pub(crate) fn new_ip_socket(
45
addr: SocketAddr,
@@ -64,32 +65,99 @@ pub(crate) fn new_socket(
6465
socket
6566
}
6667

67-
pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) {
68-
use std::mem::size_of_val;
68+
/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
69+
/// SocketAddr* types into their system representation. The benefit of this specific
70+
/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
71+
/// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
72+
#[repr(C)]
73+
pub(crate) union SocketAddrCRepr {
74+
v4: libc::sockaddr_in,
75+
v6: libc::sockaddr_in6,
76+
}
77+
78+
impl SocketAddrCRepr {
79+
pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
80+
self as *const _ as *const libc::sockaddr
81+
}
82+
}
6983

84+
/// Converts a Rust `SocketAddr` into the system representation.
85+
pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
7086
match addr {
71-
SocketAddr::V4(ref addr) => (
72-
addr as *const _ as *const libc::sockaddr,
73-
size_of_val(addr) as libc::socklen_t,
74-
),
75-
SocketAddr::V6(ref addr) => (
76-
addr as *const _ as *const libc::sockaddr,
77-
size_of_val(addr) as libc::socklen_t,
78-
),
87+
SocketAddr::V4(ref addr) => {
88+
// `s_addr` is stored as BE on all machine and the array is in BE order.
89+
// So the native endian conversion method is used so that it's never swapped.
90+
let sin_addr = libc::in_addr { s_addr: u32::from_ne_bytes(addr.ip().octets()) };
91+
92+
let sockaddr_in = libc::sockaddr_in {
93+
sin_family: libc::AF_INET as libc::sa_family_t,
94+
sin_port: addr.port().to_be(),
95+
sin_addr,
96+
sin_zero: [0; 8],
97+
#[cfg(any(
98+
target_os = "dragonfly",
99+
target_os = "freebsd",
100+
target_os = "ios",
101+
target_os = "macos",
102+
target_os = "netbsd",
103+
target_os = "openbsd"
104+
))]
105+
sin_len: 0,
106+
};
107+
108+
let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
109+
(sockaddr, mem::size_of::<libc::sockaddr_in>() as libc::socklen_t)
110+
}
111+
SocketAddr::V6(ref addr) => {
112+
let sockaddr_in6 = libc::sockaddr_in6 {
113+
sin6_family: libc::AF_INET6 as libc::sa_family_t,
114+
sin6_port: addr.port().to_be(),
115+
sin6_addr: libc::in6_addr { s6_addr: addr.ip().octets() },
116+
sin6_flowinfo: addr.flowinfo(),
117+
sin6_scope_id: addr.scope_id(),
118+
#[cfg(any(
119+
target_os = "dragonfly",
120+
target_os = "freebsd",
121+
target_os = "ios",
122+
target_os = "macos",
123+
target_os = "netbsd",
124+
target_os = "openbsd"
125+
))]
126+
sin6_len: 0,
127+
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
128+
__sin6_src_id: 0,
129+
};
130+
131+
let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
132+
(sockaddr, mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t)
133+
}
79134
}
80135
}
81136

82-
/// `storage` must be initialised to `sockaddr_in` or `sockaddr_in6`.
137+
/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
138+
///
139+
/// # Safety
140+
///
141+
/// `storage` must have the `ss_family` field correctly initialized.
142+
/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
83143
pub(crate) unsafe fn to_socket_addr(
84144
storage: *const libc::sockaddr_storage,
85145
) -> std::io::Result<SocketAddr> {
86146
match (*storage).ss_family as libc::c_int {
87-
libc::AF_INET => Ok(SocketAddr::V4(
88-
*(storage as *const libc::sockaddr_in as *const _),
89-
)),
90-
libc::AF_INET6 => Ok(SocketAddr::V6(
91-
*(storage as *const libc::sockaddr_in6 as *const _),
92-
)),
147+
libc::AF_INET => {
148+
// Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
149+
let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
150+
let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
151+
let port = u16::from_be(addr.sin_port);
152+
Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
153+
},
154+
libc::AF_INET6 => {
155+
// Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
156+
let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
157+
let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
158+
let port = u16::from_be(addr.sin6_port);
159+
Ok(SocketAddr::V6(SocketAddrV6::new(ip, port, addr.sin6_flowinfo, addr.sin6_scope_id)))
160+
},
93161
_ => Err(std::io::ErrorKind::InvalidInput.into()),
94162
}
95163
}

src/sys/unix/tcp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ pub(crate) fn new_v6_socket() -> io::Result<TcpSocket> {
2020

2121
pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> {
2222
let (raw_addr, raw_addr_length) = socket_addr(&addr);
23-
syscall!(bind(socket, raw_addr, raw_addr_length))?;
23+
syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length))?;
2424
Ok(())
2525
}
2626

2727
pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::TcpStream> {
2828
let (raw_addr, raw_addr_length) = socket_addr(&addr);
2929

30-
match syscall!(connect(socket, raw_addr, raw_addr_length)) {
30+
match syscall!(connect(socket, raw_addr.as_ptr(), raw_addr_length)) {
3131
Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => {
3232
Err(err)
3333
}

src/sys/unix/udp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
1111

1212
socket.and_then(|socket| {
1313
let (raw_addr, raw_addr_length) = socket_addr(&addr);
14-
syscall!(bind(socket, raw_addr, raw_addr_length))
14+
syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length))
1515
.map_err(|err| {
1616
// Close the socket if we hit an error, ignoring the error
1717
// from closing since we can't pass back two errors.

src/sys/windows/net.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use std::io;
2-
use std::mem::size_of_val;
2+
use std::mem;
33
use std::net::SocketAddr;
44
use std::sync::Once;
55

66
use winapi::ctypes::c_int;
7-
use winapi::shared::ws2def::SOCKADDR;
7+
use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR};
8+
use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR};
9+
use winapi::shared::ws2def::{AF_INET, AF_INET6, ADDRESS_FAMILY, SOCKADDR, SOCKADDR_IN};
10+
use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH, SOCKADDR_IN6_LH_u};
811
use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET};
912

1013
/// Initialise the network stack for Windows.
@@ -41,15 +44,65 @@ pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result<SOCKET
4144
})
4245
}
4346

44-
pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const SOCKADDR, c_int) {
47+
/// A type with the same memory layout as `SOCKADDR`. Used in converting Rust level
48+
/// SocketAddr* types into their system representation. The benefit of this specific
49+
/// type over using `SOCKADDR_STORAGE` is that this type is exactly as large as it
50+
/// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
51+
#[repr(C)]
52+
pub(crate) union SocketAddrCRepr {
53+
v4: SOCKADDR_IN,
54+
v6: SOCKADDR_IN6_LH,
55+
}
56+
57+
impl SocketAddrCRepr {
58+
pub(crate) fn as_ptr(&self) -> *const SOCKADDR {
59+
self as *const _ as *const SOCKADDR
60+
}
61+
}
62+
63+
pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, c_int) {
4564
match addr {
46-
SocketAddr::V4(ref addr) => (
47-
addr as *const _ as *const SOCKADDR,
48-
size_of_val(addr) as c_int,
49-
),
50-
SocketAddr::V6(ref addr) => (
51-
addr as *const _ as *const SOCKADDR,
52-
size_of_val(addr) as c_int,
53-
),
65+
SocketAddr::V4(ref addr) => {
66+
// `s_addr` is stored as BE on all machine and the array is in BE order.
67+
// So the native endian conversion method is used so that it's never swapped.
68+
let sin_addr = unsafe {
69+
let mut s_un = mem::zeroed::<in_addr_S_un>();
70+
*s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets());
71+
IN_ADDR { S_un: s_un }
72+
};
73+
74+
let sockaddr_in = SOCKADDR_IN {
75+
sin_family: AF_INET as ADDRESS_FAMILY,
76+
sin_port: addr.port().to_be(),
77+
sin_addr,
78+
sin_zero: [0; 8],
79+
};
80+
81+
let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
82+
(sockaddr, mem::size_of::<SOCKADDR_IN>() as c_int)
83+
},
84+
SocketAddr::V6(ref addr) => {
85+
let sin6_addr = unsafe {
86+
let mut u = mem::zeroed::<in6_addr_u>();
87+
*u.Byte_mut() = addr.ip().octets();
88+
IN6_ADDR { u }
89+
};
90+
let u = unsafe {
91+
let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>();
92+
*u.sin6_scope_id_mut() = addr.scope_id();
93+
u
94+
};
95+
96+
let sockaddr_in6 = SOCKADDR_IN6_LH {
97+
sin6_family: AF_INET6 as ADDRESS_FAMILY,
98+
sin6_port: addr.port().to_be(),
99+
sin6_addr,
100+
sin6_flowinfo: addr.flowinfo(),
101+
u,
102+
};
103+
104+
let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
105+
(sockaddr, mem::size_of::<SOCKADDR_IN6_LH>() as c_int)
106+
}
54107
}
55108
}

src/sys/windows/tcp.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use std::io;
22
use std::mem::size_of;
3-
use std::net::{self, SocketAddr, SocketAddrV4, SocketAddrV6};
3+
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
44
use std::time::Duration;
55
use std::convert::TryInto;
66
use std::os::windows::io::FromRawSocket;
77
use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64.
88

99
use winapi::ctypes::{c_char, c_int, c_ushort};
10-
use winapi::shared::ws2def::{SOCKADDR_STORAGE, AF_INET, SOCKADDR_IN};
10+
use winapi::shared::ws2def::{SOCKADDR_STORAGE, AF_INET, AF_INET6, SOCKADDR_IN};
1111
use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH;
1212

1313
use winapi::shared::minwindef::{BOOL, TRUE, FALSE};
@@ -35,7 +35,7 @@ pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> {
3535

3636
let (raw_addr, raw_addr_length) = socket_addr(&addr);
3737
syscall!(
38-
bind(socket, raw_addr, raw_addr_length),
38+
bind(socket, raw_addr.as_ptr(), raw_addr_length),
3939
PartialEq::eq,
4040
SOCKET_ERROR
4141
)?;
@@ -48,7 +48,7 @@ pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result<net::Tc
4848
let (raw_addr, raw_addr_length) = socket_addr(&addr);
4949

5050
let res = syscall!(
51-
connect(socket, raw_addr, raw_addr_length),
51+
connect(socket, raw_addr.as_ptr(), raw_addr_length),
5252
PartialEq::eq,
5353
SOCKET_ERROR
5454
);
@@ -108,23 +108,32 @@ pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result<bool> {
108108
}
109109

110110
pub(crate) fn get_localaddr(socket: TcpSocket) -> io::Result<SocketAddr> {
111-
let mut addr: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() };
112-
let mut length = std::mem::size_of_val(&addr) as c_int;
111+
let mut storage: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() };
112+
let mut length = std::mem::size_of_val(&storage) as c_int;
113113

114114
match unsafe { getsockname(
115115
socket,
116-
&mut addr as *mut _ as *mut _,
116+
&mut storage as *mut _ as *mut _,
117117
&mut length
118118
) } {
119119
SOCKET_ERROR => Err(io::Error::last_os_error()),
120120
_ => {
121-
let storage: *const SOCKADDR_STORAGE = (&addr) as *const _;
122-
if addr.ss_family as c_int == AF_INET {
123-
let sock_addr : SocketAddrV4 = unsafe { *(storage as *const SOCKADDR_IN as *const _) };
124-
Ok(sock_addr.into())
121+
if storage.ss_family as c_int == AF_INET {
122+
// Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
123+
let addr: &SOCKADDR_IN = unsafe { &*(&storage as *const _ as *const SOCKADDR_IN) };
124+
let ip_bytes = unsafe { addr.sin_addr.S_un.S_un_b() };
125+
let ip = Ipv4Addr::from([ip_bytes.s_b1, ip_bytes.s_b2, ip_bytes.s_b3, ip_bytes.s_b4]);
126+
let port = u16::from_be(addr.sin_port);
127+
Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
128+
} else if storage.ss_family as c_int == AF_INET6 {
129+
// Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
130+
let addr: &SOCKADDR_IN6_LH = unsafe { &*(&storage as *const _ as *const SOCKADDR_IN6_LH) };
131+
let ip = Ipv6Addr::from(*unsafe { addr.sin6_addr.u.Byte() });
132+
let port = u16::from_be(addr.sin6_port);
133+
let scope_id = unsafe { *addr.u.sin6_scope_id() };
134+
Ok(SocketAddr::V6(SocketAddrV6::new(ip, port, addr.sin6_flowinfo, scope_id)))
125135
} else {
126-
let sock_addr : SocketAddrV6 = unsafe { *(storage as *const SOCKADDR_IN6_LH as *const _) };
127-
Ok(sock_addr.into())
136+
Err(std::io::ErrorKind::InvalidInput.into())
128137
}
129138
},
130139
}

src/sys/windows/udp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
1212
new_ip_socket(addr, SOCK_DGRAM).and_then(|socket| {
1313
let (raw_addr, raw_addr_length) = socket_addr(&addr);
1414
syscall!(
15-
win_bind(socket, raw_addr, raw_addr_length,),
15+
win_bind(socket, raw_addr.as_ptr(), raw_addr_length,),
1616
PartialEq::eq,
1717
SOCKET_ERROR
1818
)

0 commit comments

Comments
 (0)