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