diff --git a/src/socket.rs b/src/socket.rs index 2b4a48c..17566d8 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -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(|_| ()) } } @@ -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(|_| ()) } } @@ -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::() 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::(); + *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::() 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::() }; + 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::() as c::socklen_t) +} + +#[cfg(windows)] +fn addr2raw_v6(addr: &SocketAddrV6) -> (SocketAddrCRepr, c::socklen_t) { + let sin6_addr = unsafe { + let mut u = mem::zeroed::(); + *u.Byte_mut() = addr.ip().octets(); + c::IN6_ADDR { u } + }; + let scope_id = unsafe { + let mut u = mem::zeroed::(); + *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::() as c::socklen_t) +} + fn raw2addr(storage: &c::sockaddr_storage, len: c::socklen_t) -> io::Result { match storage.ss_family as c_int { c::AF_INET => { diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs index b2da96d..7086bec 100644 --- a/src/sys/windows/mod.rs +++ b/src/sys/windows/mod.rs @@ -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;