Skip to content

Commit

Permalink
Don't assume memory layout of std::net::SocketAddr
Browse files Browse the repository at this point in the history
Fixes #120
  • Loading branch information
faern committed Nov 7, 2020
1 parent 99da5c2 commit d2c15de
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
169 changes: 128 additions & 41 deletions src/sockaddr.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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::<sockaddr_storage>::uninit();
let mut storage = MaybeUninit::<sockaddr_storage>::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,
}
}

Expand Down Expand Up @@ -120,33 +124,60 @@ impl SockAddr {
}
}

unsafe fn as_<T>(&self, family: sa_family_t) -> Option<T> {
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<SocketAddrV4> {
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<SocketAddrV6> {
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<SocketAddr> {
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
}
Expand All @@ -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::<SocketAddrV4, sockaddr_in>(v4);
mem::transmute::<SocketAddrV6, sockaddr_in6>(v6);
}
}

impl From<SocketAddrV4> for SockAddr {
fn from(addr: SocketAddrV4) -> SockAddr {
unsafe {
SockAddr::from_raw_parts(
&addr as *const _ as *const _,
mem::size_of::<SocketAddrV4>() 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::<in_addr_S_un>();
*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::<sockaddr_storage>::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::<sockaddr_in>() as socklen_t,
}
}
}

impl From<SocketAddrV6> for SockAddr {
fn from(addr: SocketAddrV6) -> SockAddr {
unsafe {
SockAddr::from_raw_parts(
&addr as *const _ as *const _,
mem::size_of::<SocketAddrV6>() 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::<in6_addr_u>();
*u.Byte_mut() = addr.ip().octets();
in6_addr { u }
};
#[cfg(windows)]
let u = unsafe {
let mut u = mem::zeroed::<SOCKADDR_IN6_LH_u>();
*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::<sockaddr_storage>::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::<sockaddr_in6>() as socklen_t,
}
}
}
Expand Down

0 comments on commit d2c15de

Please sign in to comment.