From 14f992aca1a8dad41a780a819ac2fc5086f4c617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sat, 7 Nov 2020 15:35:25 +0100 Subject: [PATCH 1/7] Safely convert SocketAddr -> system representation --- src/socket.rs | 101 ++++++++++++++++++++++++++++++++++++++--- src/sys/windows/mod.rs | 1 + 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index 2b4a48c..aca8366 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -32,7 +32,7 @@ impl Socket { pub fn bind(&self, addr: &SocketAddr) -> io::Result<()> { let (addr, len) = addr2raw(addr); unsafe { - ::cvt(c::bind(self.inner.raw(), addr, len as c::socklen_t)).map(|_| ()) + ::cvt(c::bind(self.inner.raw(), addr.as_ptr(), len as c::socklen_t)).map(|_| ()) } } @@ -45,7 +45,7 @@ impl Socket { pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { let (addr, len) = addr2raw(addr); unsafe { - ::cvt(c::connect(self.inner.raw(), addr, len)).map(|_| ()) + ::cvt(c::connect(self.inner.raw(), addr.as_ptr(), len)).map(|_| ()) } } @@ -84,13 +84,100 @@ impl ::IntoInner for Socket { fn into_inner(self) -> sys::Socket { self.inner } } -fn addr2raw(addr: &SocketAddr) -> (*const c::sockaddr, c::socklen_t) { +/// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `c::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: c::sockaddr_in, + v6: c::sockaddr_in6, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const c::sockaddr { + self as *const _ as *const c::sockaddr + } +} + +fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { match *addr { - SocketAddr::V4(ref a) => { - (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) + SocketAddr::V4(addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + #[cfg(unix)] + let sin_addr = c::in_addr { + s_addr: u32::from_ne_bytes(addr.ip().octets()), + }; + #[cfg(windows)] + let sin_addr = unsafe { + let mut s_un = mem::zeroed::(); + *s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + c::IN_ADDR { S_un: s_un } + }; + + let sockaddr_in = c::sockaddr_in { + sin_family: c::AF_INET as c::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin_len: 0, + }; + + let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + (sockaddr, mem::size_of::() as c::socklen_t) } - SocketAddr::V6(ref a) => { - (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t) + SocketAddr::V6(addr) => { + #[cfg(unix)] + let sin6_addr = c::in6_addr { + s6_addr: addr.ip().octets(), + ..unsafe { mem::zeroed() } + }; + #[cfg(windows)] + let sin6_addr = unsafe { + let mut u = mem::zeroed::(); + *u.Byte_mut() = addr.ip().octets(); + c::IN6_ADDR { u } + }; + #[cfg(windows)] + let u = unsafe { + let mut u = mem::zeroed::(); + *u.sin6_scope_id_mut() = addr.scope_id(); + u + }; + + let sockaddr_in6 = c::sockaddr_in6 { + sin6_family: c::AF_INET6 as c::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr, + sin6_flowinfo: addr.flowinfo(), + #[cfg(unix)] + sin6_scope_id: addr.scope_id(), + #[cfg(windows)] + u, + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin6_len: 0, + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + __sin6_src_id: 0, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + (sockaddr, mem::size_of::() as c::socklen_t) } } } diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index b2da96d..7086bec 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -34,6 +34,7 @@ pub mod c { pub use winapi::shared::ws2def::SOCKADDR as sockaddr; pub use winapi::shared::ws2def::SOCKADDR_STORAGE as sockaddr_storage; pub use winapi::shared::ws2def::SOCKADDR_IN as sockaddr_in; + pub use winapi::shared::ws2def::ADDRESS_FAMILY as sa_family_t; pub use winapi::shared::ws2ipdef::*; pub use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6; pub use winapi::shared::ws2ipdef::IP_MREQ as ip_mreq; From 7d0a351505dc654facf580da3f4b8e7c6d0ecbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sat, 7 Nov 2020 16:04:22 +0100 Subject: [PATCH 2/7] Use transmute instead of u32::from_ne_bytes to support older Rust --- src/socket.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index aca8366..2b2d731 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -104,15 +104,15 @@ fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { match *addr { SocketAddr::V4(addr) => { // `s_addr` is stored as BE on all machine and the array is in BE order. - // So the native endian conversion method is used so that it's never swapped. + // So just transmuting the octects to the `u32` representation works. #[cfg(unix)] let sin_addr = c::in_addr { - s_addr: u32::from_ne_bytes(addr.ip().octets()), + s_addr: unsafe { mem::transmute::<_, u32>(addr.ip().octets()) }, }; #[cfg(windows)] let sin_addr = unsafe { let mut s_un = mem::zeroed::(); - *s_un.S_addr_mut() = u32::from_ne_bytes(addr.ip().octets()); + *s_un.S_addr_mut() = mem::transmute::<_, u32>(addr.ip().octets()); c::IN_ADDR { S_un: s_un } }; From 3018422f5c5f40ffcf91943bee0ebcd7cc1f38f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sat, 7 Nov 2020 16:08:14 +0100 Subject: [PATCH 3/7] Initialize in6_addr in a way that works for older Rust --- src/socket.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index 2b2d731..9a76d7f 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -137,9 +137,10 @@ fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { } SocketAddr::V6(addr) => { #[cfg(unix)] - let sin6_addr = c::in6_addr { - s6_addr: addr.ip().octets(), - ..unsafe { mem::zeroed() } + let sin6_addr = { + let mut sin6_addr = unsafe { mem::zeroed::() }; + sin6_addr.s6_addr = addr.ip().octets(); + sin6_addr }; #[cfg(windows)] let sin6_addr = unsafe { From 575dffd170a6dbc2c9dae971af1ca9136aba1157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Mon, 23 Nov 2020 19:58:20 +0100 Subject: [PATCH 4/7] Remove unwanted part of docs --- src/socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.rs b/src/socket.rs index 9a76d7f..cb916cc 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -87,7 +87,7 @@ impl ::IntoInner for Socket { /// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level /// SocketAddr* types into their system representation. The benefit of this specific /// type over using `c::sockaddr_storage` is that this type is exactly as large as it -/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +/// needs to be and not a lot larger. #[repr(C)] pub(crate) union SocketAddrCRepr { v4: c::sockaddr_in, From e7768a2e82b2f36c6badc69150e251ea825f51a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Mon, 23 Nov 2020 20:07:09 +0100 Subject: [PATCH 5/7] Split addr2raw up into separate platform functions --- src/socket.rs | 187 +++++++++++++++++++++++++++++--------------------- 1 file changed, 108 insertions(+), 79 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index cb916cc..433e374 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -101,88 +101,117 @@ impl SocketAddrCRepr { } fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { - match *addr { - SocketAddr::V4(addr) => { - // `s_addr` is stored as BE on all machine and the array is in BE order. - // So just transmuting the octects to the `u32` representation works. - #[cfg(unix)] - let sin_addr = c::in_addr { - s_addr: unsafe { mem::transmute::<_, u32>(addr.ip().octets()) }, - }; - #[cfg(windows)] - let sin_addr = unsafe { - let mut s_un = mem::zeroed::(); - *s_un.S_addr_mut() = mem::transmute::<_, u32>(addr.ip().octets()); - c::IN_ADDR { S_un: s_un } - }; - - let sockaddr_in = c::sockaddr_in { - sin_family: c::AF_INET as c::sa_family_t, - sin_port: addr.port().to_be(), - sin_addr, - sin_zero: [0; 8], - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ))] - sin_len: 0, - }; - - let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; - (sockaddr, mem::size_of::() as c::socklen_t) - } - SocketAddr::V6(addr) => { - #[cfg(unix)] - let sin6_addr = { - let mut sin6_addr = unsafe { mem::zeroed::() }; - sin6_addr.s6_addr = addr.ip().octets(); - sin6_addr - }; - #[cfg(windows)] - let sin6_addr = unsafe { - let mut u = mem::zeroed::(); - *u.Byte_mut() = addr.ip().octets(); - c::IN6_ADDR { u } - }; - #[cfg(windows)] - let u = unsafe { - let mut u = mem::zeroed::(); - *u.sin6_scope_id_mut() = addr.scope_id(); - u - }; - - let sockaddr_in6 = c::sockaddr_in6 { - sin6_family: c::AF_INET6 as c::sa_family_t, - sin6_port: addr.port().to_be(), - sin6_addr, - sin6_flowinfo: addr.flowinfo(), - #[cfg(unix)] - sin6_scope_id: addr.scope_id(), - #[cfg(windows)] - u, - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ))] - sin6_len: 0, - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - __sin6_src_id: 0, - }; - - let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; - (sockaddr, mem::size_of::() as c::socklen_t) - } + match addr { + SocketAddr::V4(v4) => addr2raw_v4(v4), + SocketAddr::V6(v6) => addr2raw_v6(v6), } } +#[cfg(unix)] +fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) { + // `s_addr` is stored as BE on all machines and the array is in BE order. + // So just transmuting the octects to the `u32` representation works. + let sin_addr = c::in_addr { + s_addr: unsafe { mem::transmute::<_, u32>(addr.ip().octets()) }, + }; + + let sockaddr = SocketAddrCRepr { + v4: c::sockaddr_in { + sin_family: c::AF_INET as c::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin_len: 0, + }, + }; + (sockaddr, mem::size_of::() as c::socklen_t) +} + +#[cfg(windows)] +fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) { + // `S_un` is stored as BE on all machines and the array is in BE order. + // So just transmuting the octects to the `u32` representation works. + let sin_addr = unsafe { + let mut s_un = mem::zeroed::(); + *s_un.S_addr_mut() = mem::transmute::<_, u32>(addr.ip().octets()); + c::IN_ADDR { S_un: s_un } + }; + + let sockaddr = SocketAddrCRepr { + v4: c::sockaddr_in { + sin_family: c::AF_INET as c::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr, + sin_zero: [0; 8], + }, + }; + (sockaddr, mem::size_of::() as c::socklen_t) +} + +#[cfg(unix)] +fn addr2raw_v6(addr: &SocketAddrV6) -> (SocketAddrCRepr, c::socklen_t) { + let sin6_addr = { + let mut sin6_addr = unsafe { mem::zeroed::() }; + sin6_addr.s6_addr = addr.ip().octets(); + sin6_addr + }; + + let sockaddr = SocketAddrCRepr { + v6: c::sockaddr_in6 { + sin6_family: c::AF_INET6 as c::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr, + sin6_flowinfo: addr.flowinfo(), + sin6_scope_id: addr.scope_id(), + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin6_len: 0, + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + __sin6_src_id: 0, + }, + }; + (sockaddr, mem::size_of::() as c::socklen_t) +} + +#[cfg(windows)] +fn addr2raw_v6(addr: &SocketAddrV6) -> (SocketAddrCRepr, c::socklen_t) { + let sin6_addr = unsafe { + let mut u = mem::zeroed::(); + *u.Byte_mut() = addr.ip().octets(); + c::IN6_ADDR { u } + }; + let scope_id = unsafe { + let mut u = mem::zeroed::(); + *u.sin6_scope_id_mut() = addr.scope_id(); + u + }; + + let sockaddr = SocketAddrCRepr { + v6: c::sockaddr_in6 { + sin6_family: c::AF_INET6 as c::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr, + sin6_flowinfo: addr.flowinfo(), + u: scope_id, + }, + }; + (sockaddr, mem::size_of::() as c::socklen_t) +} + fn raw2addr(storage: &c::sockaddr_storage, len: c::socklen_t) -> io::Result { match storage.ss_family as c_int { c::AF_INET => { From 06ab86a12e3ce9d3094617e8e99f107243499277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Mon, 23 Nov 2020 21:02:57 +0100 Subject: [PATCH 6/7] Fix references --- src/socket.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index 433e374..9a1114e 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -102,8 +102,8 @@ impl SocketAddrCRepr { fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { match addr { - SocketAddr::V4(v4) => addr2raw_v4(v4), - SocketAddr::V6(v6) => addr2raw_v6(v6), + &SocketAddr::V4(ref v4) => addr2raw_v4(v4), + &SocketAddr::V6(ref v6) => addr2raw_v6(v6), } } From 233b4de8c1965d101505c589ca71014f6f973b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Mon, 23 Nov 2020 21:08:31 +0100 Subject: [PATCH 7/7] Replace transmutes with safe convertion --- src/socket.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/socket.rs b/src/socket.rs index 9a1114e..17566d8 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -109,10 +109,8 @@ fn addr2raw(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { #[cfg(unix)] fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) { - // `s_addr` is stored as BE on all machines and the array is in BE order. - // So just transmuting the octects to the `u32` representation works. let sin_addr = c::in_addr { - s_addr: unsafe { mem::transmute::<_, u32>(addr.ip().octets()) }, + s_addr: u32::from(*addr.ip()).to_be(), }; let sockaddr = SocketAddrCRepr { @@ -137,11 +135,9 @@ fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) { #[cfg(windows)] fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) { - // `S_un` is stored as BE on all machines and the array is in BE order. - // So just transmuting the octects to the `u32` representation works. let sin_addr = unsafe { let mut s_un = mem::zeroed::(); - *s_un.S_addr_mut() = mem::transmute::<_, u32>(addr.ip().octets()); + *s_un.S_addr_mut() = u32::from(*addr.ip()).to_be(); c::IN_ADDR { S_un: s_un } };