Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't assume memory layout of std::net::SocketAddr (0.3.x) #120

Merged
merged 12 commits into from
Nov 7, 2020
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"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added feature is so that libc::sockaddr_in6 does not have an extra __align field (used before #[repr(align(x))] was a thing.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this for Mio as well?


[target."cfg(target_os = \"redox\")".dependencies]
redox_syscall = "0.1.38"
Expand Down
159 changes: 121 additions & 38 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 @@ -120,33 +124,58 @@ 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 {
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 {
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 +197,88 @@ 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 mut storage = MaybeUninit::<sockaddr_storage>::uninit();
Thomasdezeeuw marked this conversation as resolved.
Show resolved Hide resolved
let sockaddr_in = unsafe { &mut *(storage.as_mut_ptr() as *mut sockaddr_in) };
faern marked this conversation as resolved.
Show resolved Hide resolved
Thomasdezeeuw marked this conversation as resolved.
Show resolved Hide resolved
*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,
};
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 mut storage = MaybeUninit::<sockaddr_storage>::uninit();
let sockaddr_in6 = unsafe { &mut *(storage.as_mut_ptr() as *mut sockaddr_in6) };
*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,
};
SockAddr {
storage: unsafe { storage.assume_init() },
len: mem::size_of::<sockaddr_in6>() as socklen_t,
}
}
}
Expand Down