Skip to content

Commit 085fc19

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

File tree

2 files changed

+49
-13
lines changed

2 files changed

+49
-13
lines changed

src/sys/windows/net.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
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, IN6_ADDR};
8+
use winapi::shared::ws2def::{AF_INET, AF_INET6, ADDRESS_FAMILY, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6};
89
use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET};
910

1011
/// Initialise the network stack for Windows.
@@ -41,15 +42,50 @@ pub(crate) fn new_socket(domain: c_int, socket_type: c_int) -> io::Result<SOCKET
4142
})
4243
}
4344

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

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
);

0 commit comments

Comments
 (0)