|
1 | 1 | use std::io;
|
2 |
| -use std::mem::size_of_val; |
| 2 | +use std::mem; |
3 | 3 | use std::net::SocketAddr;
|
4 | 4 | use std::sync::Once;
|
5 | 5 |
|
6 | 6 | 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; |
8 | 11 | use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET};
|
9 | 12 |
|
10 | 13 | /// 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
|
41 | 44 | })
|
42 | 45 | }
|
43 | 46 |
|
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) { |
45 | 64 | 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 | + } |
54 | 92 | }
|
55 | 93 | }
|
0 commit comments