use std::io; use std::mem::size_of; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result { let domain = match addr { SocketAddr::V4(..) => libc::AF_INET, SocketAddr::V6(..) => libc::AF_INET6, }; new_socket(domain, socket_type) } /// Create a new non-blocking socket. pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result { #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd" ))] let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; // Gives a warning for platforms without SOCK_NONBLOCK. #[allow(clippy::let_and_return)] let socket = syscall!(socket(domain, socket_type, 0)); // Mimick `libstd` and set `SO_NOSIGPIPE` on apple systems. #[cfg(target_vendor = "apple")] let socket = socket.and_then(|socket| { syscall!(setsockopt( socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, &1 as *const libc::c_int as *const libc::c_void, size_of::() as libc::socklen_t )) .map(|_| socket) }); // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. Not sure about // Solaris, couldn't find anything online. #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))] let socket = socket.and_then(|socket| { // For platforms that don't support flags in socket, we need to // set the flags ourselves. syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| socket)) .map_err(|e| { // If either of the `fcntl` calls failed, ensure the socket is // closed and return the error. let _ = syscall!(close(socket)); e }) }); socket } /// 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) => { // `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::() 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::() as libc::socklen_t; (sockaddr, socklen) } } } /// 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, ) -> io::Result { match (*storage).ss_family as libc::c_int { 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()), } }