Skip to content

Commit d32ff16

Browse files
committed
Windows: Don't assume memory layout of std::net::SocketAddr
1 parent 102b629 commit d32ff16

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

src/sys/windows/net.rs

Lines changed: 49 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;
8+
use winapi::shared::in6addr::IN6_ADDR;
9+
use winapi::shared::ws2def::{AF_INET, AF_INET6, ADDRESS_FAMILY, SOCKADDR, SOCKADDR_IN};
10+
use winapi::shared::ws2ipdef::SOCKADDR_IN6;
811
use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET};
912

1013
/// Initialise the network stack for Windows.
@@ -41,15 +44,50 @@ 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,
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 = IN_ADDR { S_un: unsafe { mem::transmute(u32::from_ne_bytes(addr.ip().octets())) } };
69+
70+
let sockaddr_in = SOCKADDR_IN {
71+
sin_family: AF_INET as ADDRESS_FAMILY,
72+
sin_port: addr.port().to_be(),
73+
sin_addr,
74+
sin_zero: [0; 8],
75+
};
76+
77+
let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
78+
(sockaddr, mem::size_of::<SOCKADDR_IN>() as c_int)
79+
},
80+
SocketAddr::V6(ref addr) => {
81+
let sockaddr_in6 = SOCKADDR_IN6 {
82+
sin6_family: AF_INET6 as ADDRESS_FAMILY,
83+
sin6_port: addr.port().to_be(),
84+
sin6_addr: IN6_ADDR { u: unsafe { mem::transmute(addr.ip().octets()) } },
85+
sin6_flowinfo: addr.flowinfo(),
86+
u: unsafe { mem::transmute(addr.scope_id()) },
87+
};
88+
89+
let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
90+
(sockaddr, mem::size_of::<SOCKADDR_IN6>() as c_int)
91+
}
5492
}
5593
}

src/sys/windows/tcp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
);

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)