Skip to content

Commit

Permalink
Do not assume memory layout of std::net::SocketAddr
Browse files Browse the repository at this point in the history
Fixes #105

Reviewed by: Thomas de Zeeuw <thomasdezeeuw@gmail.com>
  • Loading branch information
faern authored and pfmooney committed Nov 27, 2020
1 parent 77a6eb4 commit 49b43f2
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 10 deletions.
133 changes: 123 additions & 10 deletions src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(|_| ())
}
}

Expand All @@ -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(|_| ())
}
}

Expand Down Expand Up @@ -84,17 +84,130 @@ impl ::IntoInner for Socket {
fn into_inner(self) -> sys::Socket { self.inner }
}

fn addr2raw(addr: &SocketAddr) -> (*const c::sockaddr, c::socklen_t) {
match *addr {
SocketAddr::V4(ref a) => {
(a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t)
}
SocketAddr::V6(ref a) => {
(a as *const _ as *const _, mem::size_of_val(a) as 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.
#[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 v4) => addr2raw_v4(v4),
&SocketAddr::V6(ref v6) => addr2raw_v6(v6),
}
}

#[cfg(unix)]
fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) {
let sin_addr = c::in_addr {
s_addr: u32::from(*addr.ip()).to_be(),
};

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::<c::sockaddr_in>() as c::socklen_t)
}

#[cfg(windows)]
fn addr2raw_v4(addr: &SocketAddrV4) -> (SocketAddrCRepr, c::socklen_t) {
let sin_addr = unsafe {
let mut s_un = mem::zeroed::<c::in_addr_S_un>();
*s_un.S_addr_mut() = u32::from(*addr.ip()).to_be();
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::<c::sockaddr_in>() 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::<c::in6_addr>() };
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::<c::sockaddr_in6>() as c::socklen_t)
}

#[cfg(windows)]
fn addr2raw_v6(addr: &SocketAddrV6) -> (SocketAddrCRepr, c::socklen_t) {
let sin6_addr = unsafe {
let mut u = mem::zeroed::<c::in6_addr_u>();
*u.Byte_mut() = addr.ip().octets();
c::IN6_ADDR { u }
};
let scope_id = unsafe {
let mut u = mem::zeroed::<c::SOCKADDR_IN6_LH_u>();
*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::<c::sockaddr_in6>() as c::socklen_t)
}

fn raw2addr(storage: &c::sockaddr_storage, len: c::socklen_t) -> io::Result<SocketAddr> {
match storage.ss_family as c_int {
c::AF_INET => {
Expand Down
1 change: 1 addition & 0 deletions src/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 49b43f2

Please sign in to comment.