diff options
Diffstat (limited to 'src/sys/unix/net.rs')
-rw-r--r-- | src/sys/unix/net.rs | 140 |
1 files changed, 103 insertions, 37 deletions
diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs index 2671b42..2f8d618 100644 --- a/src/sys/unix/net.rs +++ b/src/sys/unix/net.rs @@ -1,11 +1,8 @@ -#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))] -use std::net::SocketAddr; +use std::io; +use std::mem::size_of; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -#[cfg(all(feature = "os-poll", any(feature = "udp")))] -pub(crate) fn new_ip_socket( - addr: SocketAddr, - socket_type: libc::c_int, -) -> std::io::Result<libc::c_int> { +pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> { let domain = match addr { SocketAddr::V4(..) => libc::AF_INET, SocketAddr::V6(..) => libc::AF_INET6, @@ -15,14 +12,7 @@ pub(crate) fn new_ip_socket( } /// Create a new non-blocking socket. -#[cfg(all( - feature = "os-poll", - any(feature = "tcp", feature = "udp", feature = "uds") -))] -pub(crate) fn new_socket( - domain: libc::c_int, - socket_type: libc::c_int, -) -> std::io::Result<libc::c_int> { +pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> { #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -46,7 +36,7 @@ pub(crate) fn new_socket( libc::SOL_SOCKET, libc::SO_NOSIGPIPE, &1 as *const libc::c_int as *const libc::c_void, - std::mem::size_of::<libc::c_int>() as libc::socklen_t + size_of::<libc::c_int>() as libc::socklen_t )) .map(|_| socket) }); @@ -70,34 +60,110 @@ pub(crate) fn new_socket( socket } -#[cfg(all(feature = "os-poll", any(feature = "tcp", feature = "udp")))] -pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) { - use std::mem::size_of_val; +/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized cleaner from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: libc::sockaddr_in, + v6: libc::sockaddr_in6, +} + +impl SocketAddrCRepr { + pub(crate) fn as_ptr(&self) -> *const libc::sockaddr { + self as *const _ as *const libc::sockaddr + } +} +/// Converts a Rust `SocketAddr` into the system representation. +pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) { match addr { - SocketAddr::V4(ref addr) => ( - addr as *const _ as *const libc::sockaddr, - size_of_val(addr) as libc::socklen_t, - ), - SocketAddr::V6(ref addr) => ( - addr as *const _ as *const libc::sockaddr, - size_of_val(addr) as libc::socklen_t, - ), + SocketAddr::V4(ref addr) => { + // `s_addr` is stored as BE on all machine and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + let sin_addr = libc::in_addr { + s_addr: u32::from_ne_bytes(addr.ip().octets()), + }; + + let sockaddr_in = libc::sockaddr_in { + sin_family: libc::AF_INET as libc::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 sockaddr = SocketAddrCRepr { v4: sockaddr_in }; + let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t; + (sockaddr, socklen) + } + SocketAddr::V6(ref addr) => { + let sockaddr_in6 = libc::sockaddr_in6 { + sin6_family: libc::AF_INET6 as libc::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr: libc::in6_addr { + s6_addr: addr.ip().octets(), + }, + 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, + }; + + let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; + let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t; + (sockaddr, socklen) + } } } -/// `storage` must be initialised to `sockaddr_in` or `sockaddr_in6`. -#[cfg(all(feature = "os-poll", feature = "tcp"))] +/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`. +/// +/// # Safety +/// +/// `storage` must have the `ss_family` field correctly initialized. +/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`. pub(crate) unsafe fn to_socket_addr( storage: *const libc::sockaddr_storage, -) -> std::io::Result<SocketAddr> { +) -> io::Result<SocketAddr> { match (*storage).ss_family as libc::c_int { - libc::AF_INET => Ok(SocketAddr::V4( - *(storage as *const libc::sockaddr_in as *const _), - )), - libc::AF_INET6 => Ok(SocketAddr::V6( - *(storage as *const libc::sockaddr_in6 as *const _), - )), - _ => Err(std::io::ErrorKind::InvalidInput.into()), + libc::AF_INET => { + // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. + let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in); + let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); + let port = u16::from_be(addr.sin_port); + Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) + } + libc::AF_INET6 => { + // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. + let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6); + let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr); + let port = u16::from_be(addr.sin6_port); + Ok(SocketAddr::V6(SocketAddrV6::new( + ip, + port, + addr.sin6_flowinfo, + addr.sin6_scope_id, + ))) + } + _ => Err(io::ErrorKind::InvalidInput.into()), } } |