1 | // Copyright 2015 The Rust Project Developers. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
6 | // option. This file may not be copied, modified, or distributed |
7 | // except according to those terms. |
8 | |
9 | //! Utilities for creating and using sockets. |
10 | //! |
11 | //! The goal of this crate is to create and use a socket using advanced |
12 | //! configuration options (those that are not available in the types in the |
13 | //! standard library) without using any unsafe code. |
14 | //! |
15 | //! This crate provides as direct as possible access to the system's |
16 | //! functionality for sockets, this means little effort to provide |
17 | //! cross-platform utilities. It is up to the user to know how to use sockets |
18 | //! when using this crate. *If you don't know how to create a socket using |
19 | //! libc/system calls then this crate is not for you*. Most, if not all, |
20 | //! functions directly relate to the equivalent system call with no error |
21 | //! handling applied, so no handling errors such as [`EINTR`]. As a result using |
22 | //! this crate can be a little wordy, but it should give you maximal flexibility |
23 | //! over configuration of sockets. |
24 | //! |
25 | //! [`EINTR`]: std::io::ErrorKind::Interrupted |
26 | //! |
27 | //! # Examples |
28 | //! |
29 | //! ```no_run |
30 | //! # fn main() -> std::io::Result<()> { |
31 | //! use std::net::{SocketAddr, TcpListener}; |
32 | //! use socket2::{Socket, Domain, Type}; |
33 | //! |
34 | //! // Create a TCP listener bound to two addresses. |
35 | //! let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?; |
36 | //! |
37 | //! socket.set_only_v6(false)?; |
38 | //! let address: SocketAddr = "[::1]:12345" .parse().unwrap(); |
39 | //! socket.bind(&address.into())?; |
40 | //! socket.listen(128)?; |
41 | //! |
42 | //! let listener: TcpListener = socket.into(); |
43 | //! // ... |
44 | //! # drop(listener); |
45 | //! # Ok(()) } |
46 | //! ``` |
47 | //! |
48 | //! ## Features |
49 | //! |
50 | //! This crate has a single feature `all`, which enables all functions even ones |
51 | //! that are not available on all OSs. |
52 | |
53 | #![doc (html_root_url = "https://docs.rs/socket2/0.4" )] |
54 | #![deny (missing_docs, missing_debug_implementations, rust_2018_idioms)] |
55 | // Show required OS/features on docs.rs. |
56 | #![cfg_attr (docsrs, feature(doc_cfg))] |
57 | // Disallow warnings when running tests. |
58 | #![cfg_attr (test, deny(warnings))] |
59 | // Disallow warnings in examples. |
60 | #![doc (test(attr(deny(warnings))))] |
61 | |
62 | use std::fmt; |
63 | use std::mem::MaybeUninit; |
64 | use std::net::SocketAddr; |
65 | use std::ops::{Deref, DerefMut}; |
66 | use std::time::Duration; |
67 | |
68 | /// Macro to implement `fmt::Debug` for a type, printing the constant names |
69 | /// rather than a number. |
70 | /// |
71 | /// Note this is used in the `sys` module and thus must be defined before |
72 | /// defining the modules. |
73 | macro_rules! impl_debug { |
74 | ( |
75 | // Type name for which to implement `fmt::Debug`. |
76 | $type: path, |
77 | $( |
78 | $(#[$target: meta])* |
79 | // The flag(s) to check. |
80 | // Need to specific the libc crate because Windows doesn't use |
81 | // `libc` but `winapi`. |
82 | $libc: ident :: $flag: ident |
83 | ),+ $(,)* |
84 | ) => { |
85 | impl std::fmt::Debug for $type { |
86 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
87 | let string = match self.0 { |
88 | $( |
89 | $(#[$target])* |
90 | $libc :: $flag => stringify!($flag), |
91 | )+ |
92 | n => return write!(f, "{}" , n), |
93 | }; |
94 | f.write_str(string) |
95 | } |
96 | } |
97 | }; |
98 | } |
99 | |
100 | /// Macro to convert from one network type to another. |
101 | macro_rules! from { |
102 | ($from: ty, $for: ty) => { |
103 | impl From<$from> for $for { |
104 | fn from(socket: $from) -> $for { |
105 | #[cfg(unix)] |
106 | unsafe { |
107 | <$for>::from_raw_fd(socket.into_raw_fd()) |
108 | } |
109 | #[cfg(windows)] |
110 | unsafe { |
111 | <$for>::from_raw_socket(socket.into_raw_socket()) |
112 | } |
113 | } |
114 | } |
115 | }; |
116 | } |
117 | |
118 | mod sockaddr; |
119 | mod socket; |
120 | mod sockref; |
121 | |
122 | #[cfg_attr (unix, path = "sys/unix.rs" )] |
123 | #[cfg_attr (windows, path = "sys/windows.rs" )] |
124 | mod sys; |
125 | |
126 | #[cfg (not(any(windows, unix)))] |
127 | compile_error!("Socket2 doesn't support the compile target" ); |
128 | |
129 | use sys::c_int; |
130 | |
131 | pub use sockaddr::SockAddr; |
132 | pub use socket::Socket; |
133 | pub use sockref::SockRef; |
134 | |
135 | #[cfg (not(any( |
136 | target_os = "haiku" , |
137 | target_os = "illumos" , |
138 | target_os = "netbsd" , |
139 | target_os = "redox" , |
140 | target_os = "solaris" , |
141 | )))] |
142 | pub use socket::InterfaceIndexOrAddress; |
143 | |
144 | /// Specification of the communication domain for a socket. |
145 | /// |
146 | /// This is a newtype wrapper around an integer which provides a nicer API in |
147 | /// addition to an injection point for documentation. Convenience constants such |
148 | /// as [`Domain::IPV4`], [`Domain::IPV6`], etc, are provided to avoid reaching |
149 | /// into libc for various constants. |
150 | /// |
151 | /// This type is freely interconvertible with C's `int` type, however, if a raw |
152 | /// value needs to be provided. |
153 | #[derive (Copy, Clone, Eq, PartialEq)] |
154 | pub struct Domain(c_int); |
155 | |
156 | impl Domain { |
157 | /// Domain for IPv4 communication, corresponding to `AF_INET`. |
158 | pub const IPV4: Domain = Domain(sys::AF_INET); |
159 | |
160 | /// Domain for IPv6 communication, corresponding to `AF_INET6`. |
161 | pub const IPV6: Domain = Domain(sys::AF_INET6); |
162 | |
163 | /// Returns the correct domain for `address`. |
164 | pub const fn for_address(address: SocketAddr) -> Domain { |
165 | match address { |
166 | SocketAddr::V4(_) => Domain::IPV4, |
167 | SocketAddr::V6(_) => Domain::IPV6, |
168 | } |
169 | } |
170 | } |
171 | |
172 | impl From<c_int> for Domain { |
173 | fn from(d: c_int) -> Domain { |
174 | Domain(d) |
175 | } |
176 | } |
177 | |
178 | impl From<Domain> for c_int { |
179 | fn from(d: Domain) -> c_int { |
180 | d.0 |
181 | } |
182 | } |
183 | |
184 | /// Specification of communication semantics on a socket. |
185 | /// |
186 | /// This is a newtype wrapper around an integer which provides a nicer API in |
187 | /// addition to an injection point for documentation. Convenience constants such |
188 | /// as [`Type::STREAM`], [`Type::DGRAM`], etc, are provided to avoid reaching |
189 | /// into libc for various constants. |
190 | /// |
191 | /// This type is freely interconvertible with C's `int` type, however, if a raw |
192 | /// value needs to be provided. |
193 | #[derive (Copy, Clone, Eq, PartialEq)] |
194 | pub struct Type(c_int); |
195 | |
196 | impl Type { |
197 | /// Type corresponding to `SOCK_STREAM`. |
198 | /// |
199 | /// Used for protocols such as TCP. |
200 | pub const STREAM: Type = Type(sys::SOCK_STREAM); |
201 | |
202 | /// Type corresponding to `SOCK_DGRAM`. |
203 | /// |
204 | /// Used for protocols such as UDP. |
205 | pub const DGRAM: Type = Type(sys::SOCK_DGRAM); |
206 | |
207 | /// Type corresponding to `SOCK_SEQPACKET`. |
208 | #[cfg (feature = "all" )] |
209 | #[cfg_attr (docsrs, doc(cfg(feature = "all" )))] |
210 | pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET); |
211 | |
212 | /// Type corresponding to `SOCK_RAW`. |
213 | #[cfg (all(feature = "all" , not(target_os = "redox" )))] |
214 | #[cfg_attr (docsrs, doc(cfg(all(feature = "all" , not(target_os = "redox" )))))] |
215 | pub const RAW: Type = Type(sys::SOCK_RAW); |
216 | } |
217 | |
218 | impl From<c_int> for Type { |
219 | fn from(t: c_int) -> Type { |
220 | Type(t) |
221 | } |
222 | } |
223 | |
224 | impl From<Type> for c_int { |
225 | fn from(t: Type) -> c_int { |
226 | t.0 |
227 | } |
228 | } |
229 | |
230 | /// Protocol specification used for creating sockets via `Socket::new`. |
231 | /// |
232 | /// This is a newtype wrapper around an integer which provides a nicer API in |
233 | /// addition to an injection point for documentation. |
234 | /// |
235 | /// This type is freely interconvertible with C's `int` type, however, if a raw |
236 | /// value needs to be provided. |
237 | #[derive (Copy, Clone, Eq, PartialEq)] |
238 | pub struct Protocol(c_int); |
239 | |
240 | impl Protocol { |
241 | /// Protocol corresponding to `ICMPv4`. |
242 | pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP); |
243 | |
244 | /// Protocol corresponding to `ICMPv6`. |
245 | pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6); |
246 | |
247 | /// Protocol corresponding to `TCP`. |
248 | pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP); |
249 | |
250 | /// Protocol corresponding to `UDP`. |
251 | pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP); |
252 | } |
253 | |
254 | impl From<c_int> for Protocol { |
255 | fn from(p: c_int) -> Protocol { |
256 | Protocol(p) |
257 | } |
258 | } |
259 | |
260 | impl From<Protocol> for c_int { |
261 | fn from(p: Protocol) -> c_int { |
262 | p.0 |
263 | } |
264 | } |
265 | |
266 | /// Flags for incoming messages. |
267 | /// |
268 | /// Flags provide additional information about incoming messages. |
269 | #[cfg (not(target_os = "redox" ))] |
270 | #[cfg_attr (docsrs, doc(cfg(not(target_os = "redox" ))))] |
271 | #[derive (Copy, Clone, Eq, PartialEq)] |
272 | pub struct RecvFlags(c_int); |
273 | |
274 | #[cfg (not(target_os = "redox" ))] |
275 | impl RecvFlags { |
276 | /// Check if the message contains a truncated datagram. |
277 | /// |
278 | /// This flag is only used for datagram-based sockets, |
279 | /// not for stream sockets. |
280 | /// |
281 | /// On Unix this corresponds to the `MSG_TRUNC` flag. |
282 | /// On Windows this corresponds to the `WSAEMSGSIZE` error code. |
283 | pub const fn is_truncated(self) -> bool { |
284 | self.0 & sys::MSG_TRUNC != 0 |
285 | } |
286 | } |
287 | |
288 | /// A version of [`IoSliceMut`] that allows the buffer to be uninitialised. |
289 | /// |
290 | /// [`IoSliceMut`]: std::io::IoSliceMut |
291 | #[repr (transparent)] |
292 | pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>); |
293 | |
294 | impl<'a> fmt::Debug for MaybeUninitSlice<'a> { |
295 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
296 | fmt::Debug::fmt(self.0.as_slice(), f:fmt) |
297 | } |
298 | } |
299 | |
300 | impl<'a> MaybeUninitSlice<'a> { |
301 | /// Creates a new `MaybeUninitSlice` wrapping a byte slice. |
302 | /// |
303 | /// # Panics |
304 | /// |
305 | /// Panics on Windows if the slice is larger than 4GB. |
306 | pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> { |
307 | MaybeUninitSlice(sys::MaybeUninitSlice::new(buf)) |
308 | } |
309 | } |
310 | |
311 | impl<'a> Deref for MaybeUninitSlice<'a> { |
312 | type Target = [MaybeUninit<u8>]; |
313 | |
314 | fn deref(&self) -> &[MaybeUninit<u8>] { |
315 | self.0.as_slice() |
316 | } |
317 | } |
318 | |
319 | impl<'a> DerefMut for MaybeUninitSlice<'a> { |
320 | fn deref_mut(&mut self) -> &mut [MaybeUninit<u8>] { |
321 | self.0.as_mut_slice() |
322 | } |
323 | } |
324 | |
325 | /// Configures a socket's TCP keepalive parameters. |
326 | /// |
327 | /// See [`Socket::set_tcp_keepalive`]. |
328 | #[derive (Debug, Clone)] |
329 | pub struct TcpKeepalive { |
330 | #[cfg_attr (target_os = "openbsd" , allow(dead_code))] |
331 | time: Option<Duration>, |
332 | #[cfg (not(any( |
333 | target_os = "openbsd" , |
334 | target_os = "redox" , |
335 | target_os = "solaris" , |
336 | target_os = "nto" , |
337 | )))] |
338 | interval: Option<Duration>, |
339 | #[cfg (not(any( |
340 | target_os = "openbsd" , |
341 | target_os = "redox" , |
342 | target_os = "solaris" , |
343 | target_os = "windows" , |
344 | target_os = "nto" , |
345 | )))] |
346 | retries: Option<u32>, |
347 | } |
348 | |
349 | impl TcpKeepalive { |
350 | /// Returns a new, empty set of TCP keepalive parameters. |
351 | pub const fn new() -> TcpKeepalive { |
352 | TcpKeepalive { |
353 | time: None, |
354 | #[cfg (not(any( |
355 | target_os = "openbsd" , |
356 | target_os = "redox" , |
357 | target_os = "solaris" , |
358 | target_os = "nto" , |
359 | )))] |
360 | interval: None, |
361 | #[cfg (not(any( |
362 | target_os = "openbsd" , |
363 | target_os = "redox" , |
364 | target_os = "solaris" , |
365 | target_os = "windows" , |
366 | target_os = "nto" , |
367 | )))] |
368 | retries: None, |
369 | } |
370 | } |
371 | |
372 | /// Set the amount of time after which TCP keepalive probes will be sent on |
373 | /// idle connections. |
374 | /// |
375 | /// This will set `TCP_KEEPALIVE` on macOS and iOS, and |
376 | /// `TCP_KEEPIDLE` on all other Unix operating systems, except |
377 | /// OpenBSD and Haiku which don't support any way to set this |
378 | /// option. On Windows, this sets the value of the `tcp_keepalive` |
379 | /// struct's `keepalivetime` field. |
380 | /// |
381 | /// Some platforms specify this value in seconds, so sub-second |
382 | /// specifications may be omitted. |
383 | pub const fn with_time(self, time: Duration) -> Self { |
384 | Self { |
385 | time: Some(time), |
386 | ..self |
387 | } |
388 | } |
389 | |
390 | /// Set the value of the `TCP_KEEPINTVL` option. On Windows, this sets the |
391 | /// value of the `tcp_keepalive` struct's `keepaliveinterval` field. |
392 | /// |
393 | /// Sets the time interval between TCP keepalive probes. |
394 | /// |
395 | /// Some platforms specify this value in seconds, so sub-second |
396 | /// specifications may be omitted. |
397 | #[cfg (all( |
398 | feature = "all" , |
399 | any( |
400 | target_os = "android" , |
401 | target_os = "dragonfly" , |
402 | target_os = "freebsd" , |
403 | target_os = "fuchsia" , |
404 | target_os = "illumos" , |
405 | target_os = "linux" , |
406 | target_os = "netbsd" , |
407 | target_vendor = "apple" , |
408 | windows, |
409 | ) |
410 | ))] |
411 | #[cfg_attr ( |
412 | docsrs, |
413 | doc(cfg(all( |
414 | feature = "all" , |
415 | any( |
416 | target_os = "android" , |
417 | target_os = "dragonfly" , |
418 | target_os = "freebsd" , |
419 | target_os = "fuchsia" , |
420 | target_os = "illumos" , |
421 | target_os = "linux" , |
422 | target_os = "netbsd" , |
423 | target_vendor = "apple" , |
424 | windows, |
425 | ) |
426 | ))) |
427 | )] |
428 | pub const fn with_interval(self, interval: Duration) -> Self { |
429 | Self { |
430 | interval: Some(interval), |
431 | ..self |
432 | } |
433 | } |
434 | |
435 | /// Set the value of the `TCP_KEEPCNT` option. |
436 | /// |
437 | /// Set the maximum number of TCP keepalive probes that will be sent before |
438 | /// dropping a connection, if TCP keepalive is enabled on this socket. |
439 | #[cfg (all( |
440 | feature = "all" , |
441 | any( |
442 | doc, |
443 | target_os = "android" , |
444 | target_os = "dragonfly" , |
445 | target_os = "freebsd" , |
446 | target_os = "fuchsia" , |
447 | target_os = "illumos" , |
448 | target_os = "linux" , |
449 | target_os = "netbsd" , |
450 | target_vendor = "apple" , |
451 | ) |
452 | ))] |
453 | #[cfg_attr ( |
454 | docsrs, |
455 | doc(cfg(all( |
456 | feature = "all" , |
457 | any( |
458 | target_os = "android" , |
459 | target_os = "dragonfly" , |
460 | target_os = "freebsd" , |
461 | target_os = "fuchsia" , |
462 | target_os = "illumos" , |
463 | target_os = "linux" , |
464 | target_os = "netbsd" , |
465 | target_vendor = "apple" , |
466 | ) |
467 | ))) |
468 | )] |
469 | pub const fn with_retries(self, retries: u32) -> Self { |
470 | Self { |
471 | retries: Some(retries), |
472 | ..self |
473 | } |
474 | } |
475 | } |
476 | |