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 #106

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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