| 1 | use std::io; |
| 2 | use std::mem::size_of; |
| 3 | use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; |
| 4 | |
| 5 | pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> { |
| 6 | let domain: i32 = match addr { |
| 7 | SocketAddr::V4(..) => libc::AF_INET, |
| 8 | SocketAddr::V6(..) => libc::AF_INET6, |
| 9 | }; |
| 10 | |
| 11 | new_socket(domain, socket_type) |
| 12 | } |
| 13 | |
| 14 | /// Create a new non-blocking socket. |
| 15 | pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> { |
| 16 | #[cfg (any( |
| 17 | target_os = "android" , |
| 18 | target_os = "dragonfly" , |
| 19 | target_os = "freebsd" , |
| 20 | target_os = "hurd" , |
| 21 | target_os = "illumos" , |
| 22 | target_os = "linux" , |
| 23 | target_os = "netbsd" , |
| 24 | target_os = "openbsd" , |
| 25 | target_os = "solaris" , |
| 26 | target_os = "hermit" , |
| 27 | ))] |
| 28 | let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; |
| 29 | #[cfg (target_os = "nto" )] |
| 30 | let socket_type = socket_type | libc::SOCK_CLOEXEC; |
| 31 | |
| 32 | let socket = syscall!(socket(domain, socket_type, 0))?; |
| 33 | |
| 34 | // Mimick `libstd` and set `SO_NOSIGPIPE` on apple systems. |
| 35 | #[cfg (any( |
| 36 | target_os = "ios" , |
| 37 | target_os = "macos" , |
| 38 | target_os = "tvos" , |
| 39 | target_os = "visionos" , |
| 40 | target_os = "watchos" , |
| 41 | ))] |
| 42 | if let Err(err) = syscall!(setsockopt( |
| 43 | socket, |
| 44 | libc::SOL_SOCKET, |
| 45 | libc::SO_NOSIGPIPE, |
| 46 | &1 as *const libc::c_int as *const libc::c_void, |
| 47 | size_of::<libc::c_int>() as libc::socklen_t |
| 48 | )) { |
| 49 | let _ = syscall!(close(socket)); |
| 50 | return Err(err); |
| 51 | } |
| 52 | |
| 53 | // Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. |
| 54 | #[cfg (any( |
| 55 | target_os = "ios" , |
| 56 | target_os = "macos" , |
| 57 | target_os = "tvos" , |
| 58 | target_os = "visionos" , |
| 59 | target_os = "watchos" , |
| 60 | target_os = "espidf" , |
| 61 | target_os = "vita" , |
| 62 | target_os = "nto" , |
| 63 | ))] |
| 64 | { |
| 65 | if let Err(err) = syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) { |
| 66 | let _ = syscall!(close(socket)); |
| 67 | return Err(err); |
| 68 | } |
| 69 | #[cfg (not(any(target_os = "espidf" , target_os = "vita" , target_os = "nto" )))] |
| 70 | if let Err(err) = syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)) { |
| 71 | let _ = syscall!(close(socket)); |
| 72 | return Err(err); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | Ok(socket) |
| 77 | } |
| 78 | |
| 79 | /// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level |
| 80 | /// SocketAddr* types into their system representation. The benefit of this specific |
| 81 | /// type over using `libc::sockaddr_storage` is that this type is exactly as large as it |
| 82 | /// needs to be and not a lot larger. And it can be initialized cleaner from Rust. |
| 83 | #[repr (C)] |
| 84 | pub(crate) union SocketAddrCRepr { |
| 85 | v4: libc::sockaddr_in, |
| 86 | v6: libc::sockaddr_in6, |
| 87 | } |
| 88 | |
| 89 | impl SocketAddrCRepr { |
| 90 | pub(crate) fn as_ptr(&self) -> *const libc::sockaddr { |
| 91 | self as *const _ as *const libc::sockaddr |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /// Converts a Rust `SocketAddr` into the system representation. |
| 96 | pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) { |
| 97 | match addr { |
| 98 | SocketAddr::V4(ref addr) => { |
| 99 | // `s_addr` is stored as BE on all machine and the array is in BE order. |
| 100 | // So the native endian conversion method is used so that it's never swapped. |
| 101 | let sin_addr = libc::in_addr { |
| 102 | s_addr: u32::from_ne_bytes(addr.ip().octets()), |
| 103 | }; |
| 104 | |
| 105 | let sockaddr_in = libc::sockaddr_in { |
| 106 | sin_family: libc::AF_INET as libc::sa_family_t, |
| 107 | sin_port: addr.port().to_be(), |
| 108 | sin_addr, |
| 109 | #[cfg (not(any(target_os = "haiku" , target_os = "vita" )))] |
| 110 | sin_zero: [0; 8], |
| 111 | #[cfg (target_os = "haiku" )] |
| 112 | sin_zero: [0; 24], |
| 113 | #[cfg (target_os = "vita" )] |
| 114 | sin_zero: [0; 6], |
| 115 | #[cfg (any( |
| 116 | target_os = "aix" , |
| 117 | target_os = "dragonfly" , |
| 118 | target_os = "freebsd" , |
| 119 | target_os = "haiku" , |
| 120 | target_os = "hurd" , |
| 121 | target_os = "ios" , |
| 122 | target_os = "macos" , |
| 123 | target_os = "netbsd" , |
| 124 | target_os = "openbsd" , |
| 125 | target_os = "tvos" , |
| 126 | target_os = "visionos" , |
| 127 | target_os = "watchos" , |
| 128 | target_os = "espidf" , |
| 129 | target_os = "vita" , |
| 130 | target_os = "hermit" , |
| 131 | target_os = "nto" , |
| 132 | ))] |
| 133 | sin_len: 0, |
| 134 | #[cfg (target_os = "vita" )] |
| 135 | sin_vport: addr.port().to_be(), |
| 136 | }; |
| 137 | |
| 138 | let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; |
| 139 | let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t; |
| 140 | (sockaddr, socklen) |
| 141 | } |
| 142 | SocketAddr::V6(ref addr) => { |
| 143 | let sockaddr_in6 = libc::sockaddr_in6 { |
| 144 | sin6_family: libc::AF_INET6 as libc::sa_family_t, |
| 145 | sin6_port: addr.port().to_be(), |
| 146 | sin6_addr: libc::in6_addr { |
| 147 | s6_addr: addr.ip().octets(), |
| 148 | }, |
| 149 | sin6_flowinfo: addr.flowinfo(), |
| 150 | sin6_scope_id: addr.scope_id(), |
| 151 | #[cfg (any( |
| 152 | target_os = "aix" , |
| 153 | target_os = "dragonfly" , |
| 154 | target_os = "freebsd" , |
| 155 | target_os = "haiku" , |
| 156 | target_os = "hurd" , |
| 157 | target_os = "ios" , |
| 158 | target_os = "macos" , |
| 159 | target_os = "netbsd" , |
| 160 | target_os = "openbsd" , |
| 161 | target_os = "tvos" , |
| 162 | target_os = "visionos" , |
| 163 | target_os = "watchos" , |
| 164 | target_os = "espidf" , |
| 165 | target_os = "vita" , |
| 166 | target_os = "nto" , |
| 167 | target_os = "hermit" , |
| 168 | ))] |
| 169 | sin6_len: 0, |
| 170 | #[cfg (target_os = "vita" )] |
| 171 | sin6_vport: addr.port().to_be(), |
| 172 | #[cfg (any(target_os = "illumos" , target_os = "solaris" ))] |
| 173 | __sin6_src_id: 0, |
| 174 | }; |
| 175 | |
| 176 | let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 }; |
| 177 | let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t; |
| 178 | (sockaddr, socklen) |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | /// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`. |
| 184 | /// |
| 185 | /// # Safety |
| 186 | /// |
| 187 | /// `storage` must have the `ss_family` field correctly initialized. |
| 188 | /// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`. |
| 189 | pub(crate) unsafe fn to_socket_addr( |
| 190 | storage: *const libc::sockaddr_storage, |
| 191 | ) -> io::Result<SocketAddr> { |
| 192 | match (*storage).ss_family as libc::c_int { |
| 193 | libc::AF_INET => { |
| 194 | // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in. |
| 195 | let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in); |
| 196 | let ip: Ipv4Addr = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()); |
| 197 | let port: u16 = u16::from_be(addr.sin_port); |
| 198 | Ok(SocketAddr::V4(SocketAddrV4::new(ip, port))) |
| 199 | } |
| 200 | libc::AF_INET6 => { |
| 201 | // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6. |
| 202 | let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6); |
| 203 | let ip: Ipv6Addr = Ipv6Addr::from(addr.sin6_addr.s6_addr); |
| 204 | let port: u16 = u16::from_be(addr.sin6_port); |
| 205 | Ok(SocketAddr::V6(SocketAddrV6::new( |
| 206 | ip, |
| 207 | port, |
| 208 | addr.sin6_flowinfo, |
| 209 | addr.sin6_scope_id, |
| 210 | ))) |
| 211 | } |
| 212 | _ => Err(io::ErrorKind::InvalidInput.into()), |
| 213 | } |
| 214 | } |
| 215 | |