diff --git a/Cargo.toml b/Cargo.toml index 3a1f95cc..c8bf0d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ features = ["handleapi", "ws2def", "ws2ipdef", "ws2tcpip", "minwindef"] [target."cfg(any(unix, target_os = \"redox\"))".dependencies] cfg-if = "0.1.6" -libc = "0.2.66" +libc = { version = "0.2.66", features = ["align"] } [target."cfg(target_os = \"redox\")".dependencies] redox_syscall = "0.1.38" diff --git a/src/sockaddr.rs b/src/sockaddr.rs index 7bdf38ca..434163fe 100644 --- a/src/sockaddr.rs +++ b/src/sockaddr.rs @@ -1,20 +1,24 @@ use std::fmt; use std::mem::{self, MaybeUninit}; -use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::ptr; #[cfg(any(unix, target_os = "redox"))] use libc::{ - sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET, - AF_INET6, + in6_addr, in_addr, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, + socklen_t, AF_INET, AF_INET6, }; #[cfg(windows)] +use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR as in6_addr}; +#[cfg(windows)] +use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR as in_addr}; +#[cfg(windows)] use winapi::shared::ws2def::{ ADDRESS_FAMILY as sa_family_t, AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, SOCKADDR_STORAGE as sockaddr_storage, }; #[cfg(windows)] -use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6; +use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH_u, SOCKADDR_IN6_LH as sockaddr_in6}; #[cfg(windows)] use winapi::um::ws2tcpip::socklen_t; @@ -43,17 +47,17 @@ impl fmt::Debug for SockAddr { impl SockAddr { /// Constructs a `SockAddr` from its raw components. pub unsafe fn from_raw_parts(addr: *const sockaddr, len: socklen_t) -> SockAddr { - let mut storage = MaybeUninit::::uninit(); + let mut storage = MaybeUninit::::zeroed(); ptr::copy_nonoverlapping( addr as *const _ as *const u8, - &mut storage as *mut _ as *mut u8, + storage.as_mut_ptr() as *mut u8, len as usize, ); SockAddr { // This is safe as we written the address to `storage` above. storage: storage.assume_init(), - len: len, + len, } } @@ -120,33 +124,60 @@ impl SockAddr { } } - unsafe fn as_(&self, family: sa_family_t) -> Option { - if self.storage.ss_family != family { - return None; - } - - Some(mem::transmute_copy(&self.storage)) - } - /// Returns this address as a `SocketAddrV4` if it is in the `AF_INET` /// family. pub fn as_inet(&self) -> Option { - unsafe { self.as_(AF_INET as sa_family_t) } + match self.as_std() { + Some(SocketAddr::V4(addr)) => Some(addr), + _ => None, + } } /// Returns this address as a `SocketAddrV6` if it is in the `AF_INET6` /// family. pub fn as_inet6(&self) -> Option { - unsafe { self.as_(AF_INET6 as sa_family_t) } + match self.as_std() { + Some(SocketAddr::V6(addr)) => Some(addr), + _ => None, + } } /// Returns this address as a `SocketAddr` if it is in the `AF_INET` /// or `AF_INET6` family, otherwise returns `None`. pub fn as_std(&self) -> Option { - if let Some(addr) = self.as_inet() { - Some(SocketAddr::V4(addr)) - } else if let Some(addr) = self.as_inet6() { - Some(SocketAddr::V6(addr)) + if self.storage.ss_family == AF_INET as sa_family_t { + // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. + let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in) }; + + #[cfg(unix)] + let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + #[cfg(windows)] + let ip = { + let ip_bytes = unsafe { addr.sin_addr.S_un.S_un_b() }; + Ipv4Addr::from([ip_bytes.s_b1, ip_bytes.s_b2, ip_bytes.s_b3, ip_bytes.s_b4]) + }; + let port = u16::from_be(addr.sin_port); + Some(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } else if self.storage.ss_family == AF_INET6 as sa_family_t { + // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. + let addr = unsafe { &*(&self.storage as *const _ as *const sockaddr_in6) }; + + #[cfg(unix)] + let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr); + #[cfg(windows)] + let ip = Ipv6Addr::from(*unsafe { addr.sin6_addr.u.Byte() }); + let port = u16::from_be(addr.sin6_port); + Some(SocketAddr::V6(SocketAddrV6::new( + ip, + port, + addr.sin6_flowinfo, + #[cfg(unix)] + addr.sin6_scope_id, + #[cfg(windows)] + unsafe { + *addr.u.sin6_scope_id() + }, + ))) } else { None } @@ -168,34 +199,90 @@ impl SockAddr { } } -// SocketAddrV4 and SocketAddrV6 are just wrappers around sockaddr_in and sockaddr_in6 - -// check to make sure that the sizes at least match up -fn _size_checks(v4: SocketAddrV4, v6: SocketAddrV6) { - unsafe { - mem::transmute::(v4); - mem::transmute::(v6); - } -} - impl From for SockAddr { fn from(addr: SocketAddrV4) -> SockAddr { - unsafe { - SockAddr::from_raw_parts( - &addr as *const _ as *const _, - mem::size_of::() as socklen_t, - ) + #[cfg(unix)] + let sin_addr = 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()); + in_addr { S_un: s_un } + }; + + let sockaddr_in = sockaddr_in { + sin_family: AF_INET as 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 mut storage = MaybeUninit::::zeroed(); + // Safety: A `sockaddr_in` is memory compatible with a `sockaddr_storage` + unsafe { (storage.as_mut_ptr() as *mut sockaddr_in).write(sockaddr_in) }; + SockAddr { + storage: unsafe { storage.assume_init() }, + len: mem::size_of::() as socklen_t, } } } impl From for SockAddr { fn from(addr: SocketAddrV6) -> SockAddr { - unsafe { - SockAddr::from_raw_parts( - &addr as *const _ as *const _, - mem::size_of::() as socklen_t, - ) + #[cfg(unix)] + let sin6_addr = in6_addr { + s6_addr: addr.ip().octets(), + }; + #[cfg(windows)] + let sin6_addr = unsafe { + let mut u = mem::zeroed::(); + *u.Byte_mut() = addr.ip().octets(); + 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 = sockaddr_in6 { + sin6_family: AF_INET6 as 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 mut storage = MaybeUninit::::zeroed(); + // Safety: A `sockaddr_in6` is memory compatible with a `sockaddr_storage` + unsafe { (storage.as_mut_ptr() as *mut sockaddr_in6).write(sockaddr_in6) }; + SockAddr { + storage: unsafe { storage.assume_init() }, + len: mem::size_of::() as socklen_t, } } }