1 use std::io;
2 use std::mem;
3 use std::net::SocketAddr;
4 #[cfg(unix)]
5 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
6 #[cfg(windows)]
7 use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
8 use std::time::Duration;
9 
10 use crate::net::{TcpListener, TcpStream};
11 use crate::sys;
12 
13 /// A non-blocking TCP socket used to configure a stream or listener.
14 ///
15 /// The `TcpSocket` type wraps the operating-system's socket handle. This type
16 /// is used to configure the socket before establishing a connection or start
17 /// listening for inbound connections.
18 ///
19 /// The socket will be closed when the value is dropped.
20 #[derive(Debug)]
21 pub struct TcpSocket {
22     sys: sys::tcp::TcpSocket,
23 }
24 
25 /// Configures a socket's TCP keepalive parameters.
26 #[derive(Debug, Default, Clone)]
27 pub struct TcpKeepalive {
28     pub(crate) time: Option<Duration>,
29     #[cfg(any(
30         target_os = "linux",
31         target_os = "macos",
32         target_os = "ios",
33         target_os = "freebsd",
34         target_os = "netbsd",
35         target_os = "windows",
36     ))]
37     pub(crate) interval: Option<Duration>,
38     #[cfg(any(
39         target_os = "linux",
40         target_os = "macos",
41         target_os = "ios",
42         target_os = "freebsd",
43         target_os = "netbsd",
44     ))]
45     pub(crate) retries: Option<u32>,
46 }
47 
48 impl TcpSocket {
49     /// Create a new IPv4 TCP socket.
50     ///
51     /// This calls `socket(2)`.
new_v4() -> io::Result<TcpSocket>52     pub fn new_v4() -> io::Result<TcpSocket> {
53         sys::tcp::new_v4_socket().map(|sys| TcpSocket { sys })
54     }
55 
56     /// Create a new IPv6 TCP socket.
57     ///
58     /// This calls `socket(2)`.
new_v6() -> io::Result<TcpSocket>59     pub fn new_v6() -> io::Result<TcpSocket> {
60         sys::tcp::new_v6_socket().map(|sys| TcpSocket { sys })
61     }
62 
new_for_addr(addr: SocketAddr) -> io::Result<TcpSocket>63     pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result<TcpSocket> {
64         if addr.is_ipv4() {
65             TcpSocket::new_v4()
66         } else {
67             TcpSocket::new_v6()
68         }
69     }
70 
71     /// Bind `addr` to the TCP socket.
bind(&self, addr: SocketAddr) -> io::Result<()>72     pub fn bind(&self, addr: SocketAddr) -> io::Result<()> {
73         sys::tcp::bind(self.sys, addr)
74     }
75 
76     /// Connect the socket to `addr`.
77     ///
78     /// This consumes the socket and performs the connect operation. Once the
79     /// connection completes, the socket is now a non-blocking `TcpStream` and
80     /// can be used as such.
connect(self, addr: SocketAddr) -> io::Result<TcpStream>81     pub fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
82         let stream = sys::tcp::connect(self.sys, addr)?;
83 
84         // Don't close the socket
85         mem::forget(self);
86         Ok(TcpStream::from_std(stream))
87     }
88 
89     /// Listen for inbound connections, converting the socket to a
90     /// `TcpListener`.
listen(self, backlog: u32) -> io::Result<TcpListener>91     pub fn listen(self, backlog: u32) -> io::Result<TcpListener> {
92         let listener = sys::tcp::listen(self.sys, backlog)?;
93 
94         // Don't close the socket
95         mem::forget(self);
96         Ok(TcpListener::from_std(listener))
97     }
98 
99     /// Sets the value of `SO_REUSEADDR` on this socket.
set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()>100     pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
101         sys::tcp::set_reuseaddr(self.sys, reuseaddr)
102     }
103 
104     /// Get the value of `SO_REUSEADDR` set on this socket.
get_reuseaddr(&self) -> io::Result<bool>105     pub fn get_reuseaddr(&self) -> io::Result<bool> {
106         sys::tcp::get_reuseaddr(self.sys)
107     }
108 
109     /// Sets the value of `SO_REUSEPORT` on this socket.
110     /// Only supported available in unix
111     #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
set_reuseport(&self, reuseport: bool) -> io::Result<()>112     pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
113         sys::tcp::set_reuseport(self.sys, reuseport)
114     }
115 
116     /// Get the value of `SO_REUSEPORT` set on this socket.
117     /// Only supported available in unix
118     #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
get_reuseport(&self) -> io::Result<bool>119     pub fn get_reuseport(&self) -> io::Result<bool> {
120         sys::tcp::get_reuseport(self.sys)
121     }
122 
123     /// Sets the value of `SO_LINGER` on this socket.
set_linger(&self, dur: Option<Duration>) -> io::Result<()>124     pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
125         sys::tcp::set_linger(self.sys, dur)
126     }
127 
128     /// Gets the value of `SO_LINGER` on this socket
get_linger(&self) -> io::Result<Option<Duration>>129     pub fn get_linger(&self) -> io::Result<Option<Duration>> {
130         sys::tcp::get_linger(self.sys)
131     }
132 
133     /// Sets the value of `SO_RCVBUF` on this socket.
set_recv_buffer_size(&self, size: u32) -> io::Result<()>134     pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
135         sys::tcp::set_recv_buffer_size(self.sys, size)
136     }
137 
138     /// Get the value of `SO_RCVBUF` set on this socket.
139     ///
140     /// Note that if [`set_recv_buffer_size`] has been called on this socket
141     /// previously, the value returned by this function may not be the same as
142     /// the argument provided to `set_recv_buffer_size`. This is for the
143     /// following reasons:
144     ///
145     /// * Most operating systems have minimum and maximum allowed sizes for the
146     ///   receive buffer, and will clamp the provided value if it is below the
147     ///   minimum or above the maximum. The minimum and maximum buffer sizes are
148     ///   OS-dependent.
149     /// * Linux will double the buffer size to account for internal bookkeeping
150     ///   data, and returns the doubled value from `getsockopt(2)`. As per `man
151     ///   7 socket`:
152     ///   > Sets or gets the maximum socket receive buffer in bytes. The
153     ///   > kernel doubles this value (to allow space for bookkeeping
154     ///   > overhead) when it is set using `setsockopt(2)`, and this doubled
155     ///   > value is returned by `getsockopt(2)`.
156     ///
157     /// [`set_recv_buffer_size`]: #method.set_recv_buffer_size
get_recv_buffer_size(&self) -> io::Result<u32>158     pub fn get_recv_buffer_size(&self) -> io::Result<u32> {
159         sys::tcp::get_recv_buffer_size(self.sys)
160     }
161 
162     /// Sets the value of `SO_SNDBUF` on this socket.
set_send_buffer_size(&self, size: u32) -> io::Result<()>163     pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
164         sys::tcp::set_send_buffer_size(self.sys, size)
165     }
166 
167     /// Get the value of `SO_SNDBUF` set on this socket.
168     ///
169     /// Note that if [`set_send_buffer_size`] has been called on this socket
170     /// previously, the value returned by this function may not be the same as
171     /// the argument provided to `set_send_buffer_size`. This is for the
172     /// following reasons:
173     ///
174     /// * Most operating systems have minimum and maximum allowed sizes for the
175     ///   receive buffer, and will clamp the provided value if it is below the
176     ///   minimum or above the maximum. The minimum and maximum buffer sizes are
177     ///   OS-dependent.
178     /// * Linux will double the buffer size to account for internal bookkeeping
179     ///   data, and returns the doubled value from `getsockopt(2)`. As per `man
180     ///   7 socket`:
181     ///   > Sets or gets the maximum socket send buffer in bytes. The
182     ///   > kernel doubles this value (to allow space for bookkeeping
183     ///   > overhead) when it is set using `setsockopt(2)`, and this doubled
184     ///   > value is returned by `getsockopt(2)`.
185     ///
186     /// [`set_send_buffer_size`]: #method.set_send_buffer_size
get_send_buffer_size(&self) -> io::Result<u32>187     pub fn get_send_buffer_size(&self) -> io::Result<u32> {
188         sys::tcp::get_send_buffer_size(self.sys)
189     }
190 
191     /// Sets whether keepalive messages are enabled to be sent on this socket.
192     ///
193     /// This will set the `SO_KEEPALIVE` option on this socket.
set_keepalive(&self, keepalive: bool) -> io::Result<()>194     pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
195         sys::tcp::set_keepalive(self.sys, keepalive)
196     }
197 
198     /// Returns whether or not TCP keepalive probes will be sent by this socket.
get_keepalive(&self) -> io::Result<bool>199     pub fn get_keepalive(&self) -> io::Result<bool> {
200         sys::tcp::get_keepalive(self.sys)
201     }
202 
203     /// Sets parameters configuring TCP keepalive probes for this socket.
204     ///
205     /// The supported parameters depend on the operating system, and are
206     /// configured using the [`TcpKeepalive`] struct. At a minimum, all systems
207     /// support configuring the [keepalive time]: the time after which the OS
208     /// will start sending keepalive messages on an idle connection.
209     ///
210     /// # Notes
211     ///
212     /// * This will enable TCP keepalive on this socket, if it is not already
213     ///   enabled.
214     /// * On some platforms, such as Windows, any keepalive parameters *not*
215     ///   configured by the `TcpKeepalive` struct passed to this function may be
216     ///   overwritten with their default values. Therefore, this function should
217     ///   either only be called once per socket, or the same parameters should
218     ///   be passed every time it is called.
219     ///
220     /// # Examples
221     #[cfg_attr(feature = "os-poll", doc = "```")]
222     #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")]
223     /// use mio::net::{TcpSocket, TcpKeepalive};
224     /// use std::time::Duration;
225     ///
226     /// # fn main() -> Result<(), std::io::Error> {
227     /// let socket = TcpSocket::new_v6()?;
228     /// let keepalive = TcpKeepalive::default()
229     ///     .with_time(Duration::from_secs(4));
230     ///     // Depending on the target operating system, we may also be able to
231     ///     // configure the keepalive probe interval and/or the number of retries
232     ///     // here as well.
233     ///
234     /// socket.set_keepalive_params(keepalive)?;
235     /// # Ok(()) }
236     /// ```
237     ///
238     /// [`TcpKeepalive`]: ../struct.TcpKeepalive.html
239     /// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time
set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()>240     pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> {
241         self.set_keepalive(true)?;
242         sys::tcp::set_keepalive_params(self.sys, keepalive)
243     }
244 
245     /// Returns the amount of time after which TCP keepalive probes will be sent
246     /// on idle connections.
247     ///
248     /// If `None`, then keepalive messages are disabled.
249     ///
250     /// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
251     /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
252     /// on all other Unix operating systems. On Windows, it is not possible to
253     /// access the value of TCP keepalive parameters after they have been set.
254     ///
255     /// Some platforms specify this value in seconds, so sub-second
256     /// specifications may be omitted.
257     #[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))]
258     #[cfg(not(target_os = "windows"))]
get_keepalive_time(&self) -> io::Result<Option<Duration>>259     pub fn get_keepalive_time(&self) -> io::Result<Option<Duration>> {
260         sys::tcp::get_keepalive_time(self.sys)
261     }
262 
263     /// Returns the time interval between TCP keepalive probes, if TCP keepalive is
264     /// enabled on this socket.
265     ///
266     /// If `None`, then keepalive messages are disabled.
267     ///
268     /// This returns the value of `TCP_KEEPINTVL` on supported Unix operating
269     /// systems. On Windows, it is not possible to access the value of TCP
270     /// keepalive parameters after they have been set..
271     ///
272     /// Some platforms specify this value in seconds, so sub-second
273     /// specifications may be omitted.
274     #[cfg_attr(
275         docsrs,
276         doc(cfg(any(
277             target_os = "linux",
278             target_os = "macos",
279             target_os = "ios",
280             target_os = "freebsd",
281             target_os = "netbsd",
282         )))
283     )]
284     #[cfg(any(
285         target_os = "linux",
286         target_os = "macos",
287         target_os = "ios",
288         target_os = "freebsd",
289         target_os = "netbsd",
290     ))]
get_keepalive_interval(&self) -> io::Result<Option<Duration>>291     pub fn get_keepalive_interval(&self) -> io::Result<Option<Duration>> {
292         sys::tcp::get_keepalive_interval(self.sys)
293     }
294 
295     /// Returns the maximum number of TCP keepalive probes that will be sent before
296     /// dropping a connection, if TCP keepalive is enabled on this socket.
297     ///
298     /// If `None`, then keepalive messages are disabled.
299     ///
300     /// This returns the value of `TCP_KEEPCNT` on Unix operating systems that
301     /// support this option. On Windows, it is not possible to access the value
302     /// of TCP keepalive parameters after they have been set.
303     #[cfg_attr(
304         docsrs,
305         doc(cfg(any(
306             target_os = "linux",
307             target_os = "macos",
308             target_os = "ios",
309             target_os = "freebsd",
310             target_os = "netbsd",
311         )))
312     )]
313     #[cfg(any(
314         target_os = "linux",
315         target_os = "macos",
316         target_os = "ios",
317         target_os = "freebsd",
318         target_os = "netbsd",
319     ))]
get_keepalive_retries(&self) -> io::Result<Option<u32>>320     pub fn get_keepalive_retries(&self) -> io::Result<Option<u32>> {
321         sys::tcp::get_keepalive_retries(self.sys)
322     }
323 
324     /// Returns the local address of this socket
325     ///
326     /// Will return `Err` result in windows if called before calling `bind`
get_localaddr(&self) -> io::Result<SocketAddr>327     pub fn get_localaddr(&self) -> io::Result<SocketAddr> {
328         sys::tcp::get_localaddr(self.sys)
329     }
330 }
331 
332 impl Drop for TcpSocket {
drop(&mut self)333     fn drop(&mut self) {
334         sys::tcp::close(self.sys);
335     }
336 }
337 
338 #[cfg(unix)]
339 impl IntoRawFd for TcpSocket {
into_raw_fd(self) -> RawFd340     fn into_raw_fd(self) -> RawFd {
341         let ret = self.sys;
342         // Avoid closing the socket
343         mem::forget(self);
344         ret
345     }
346 }
347 
348 #[cfg(unix)]
349 impl AsRawFd for TcpSocket {
as_raw_fd(&self) -> RawFd350     fn as_raw_fd(&self) -> RawFd {
351         self.sys
352     }
353 }
354 
355 #[cfg(unix)]
356 impl FromRawFd for TcpSocket {
357     /// Converts a `RawFd` to a `TcpSocket`.
358     ///
359     /// # Notes
360     ///
361     /// The caller is responsible for ensuring that the socket is in
362     /// non-blocking mode.
from_raw_fd(fd: RawFd) -> TcpSocket363     unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
364         TcpSocket { sys: fd }
365     }
366 }
367 
368 #[cfg(windows)]
369 impl IntoRawSocket for TcpSocket {
into_raw_socket(self) -> RawSocket370     fn into_raw_socket(self) -> RawSocket {
371         // The winapi crate defines `SOCKET` as `usize`. The Rust std
372         // conditionally defines `RawSocket` as a fixed size unsigned integer
373         // matching the pointer width. These end up being the same type but we
374         // must cast between them.
375         let ret = self.sys as RawSocket;
376 
377         // Avoid closing the socket
378         mem::forget(self);
379 
380         ret
381     }
382 }
383 
384 #[cfg(windows)]
385 impl AsRawSocket for TcpSocket {
as_raw_socket(&self) -> RawSocket386     fn as_raw_socket(&self) -> RawSocket {
387         self.sys as RawSocket
388     }
389 }
390 
391 #[cfg(windows)]
392 impl FromRawSocket for TcpSocket {
393     /// Converts a `RawSocket` to a `TcpSocket`.
394     ///
395     /// # Notes
396     ///
397     /// The caller is responsible for ensuring that the socket is in
398     /// non-blocking mode.
from_raw_socket(socket: RawSocket) -> TcpSocket399     unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
400         TcpSocket {
401             sys: socket as sys::tcp::TcpSocket,
402         }
403     }
404 }
405 
406 impl TcpKeepalive {
407     // Sets the amount of time after which TCP keepalive probes will be sent
408     /// on idle connections.
409     ///
410     /// This will set the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
411     /// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
412     /// on all other Unix operating systems. On Windows, this sets the value of
413     /// the `tcp_keepalive` struct's `keepalivetime` field.
414     ///
415     /// Some platforms specify this value in seconds, so sub-second
416     /// specifications may be omitted.
with_time(self, time: Duration) -> Self417     pub fn with_time(self, time: Duration) -> Self {
418         Self {
419             time: Some(time),
420             ..self
421         }
422     }
423 
424     /// Sets the time interval between TCP keepalive probes.
425     /// This sets the value of `TCP_KEEPINTVL` on supported Unix operating
426     /// systems. On Windows, this sets the value of the `tcp_keepalive` struct's
427     /// `keepaliveinterval` field.
428     ///
429     /// Some platforms specify this value in seconds, so sub-second
430     /// specifications may be omitted.
431     #[cfg_attr(
432         docsrs,
433         doc(cfg(any(
434             target_os = "linux",
435             target_os = "macos",
436             target_os = "ios",
437             target_os = "freebsd",
438             target_os = "netbsd",
439             target_os = "windows"
440         )))
441     )]
442     #[cfg(any(
443         target_os = "linux",
444         target_os = "macos",
445         target_os = "ios",
446         target_os = "freebsd",
447         target_os = "netbsd",
448         target_os = "windows"
449     ))]
with_interval(self, interval: Duration) -> Self450     pub fn with_interval(self, interval: Duration) -> Self {
451         Self {
452             interval: Some(interval),
453             ..self
454         }
455     }
456 
457     /// Sets the maximum number of TCP keepalive probes that will be sent before
458     /// dropping a connection, if TCP keepalive is enabled on this socket.
459     ///
460     /// This will set the value of `TCP_KEEPCNT` on Unix operating systems that
461     /// support this option.
462     #[cfg_attr(
463         docsrs,
464         doc(cfg(any(
465             target_os = "linux",
466             target_os = "macos",
467             target_os = "ios",
468             target_os = "freebsd",
469             target_os = "netbsd",
470         )))
471     )]
472     #[cfg(any(
473         target_os = "linux",
474         target_os = "macos",
475         target_os = "ios",
476         target_os = "freebsd",
477         target_os = "netbsd",
478     ))]
with_retries(self, retries: u32) -> Self479     pub fn with_retries(self, retries: u32) -> Self {
480         Self {
481             retries: Some(retries),
482             ..self
483         }
484     }
485 
486     /// Returns a new, empty set of TCP keepalive parameters.
new() -> Self487     pub fn new() -> Self {
488         Self::default()
489     }
490 }
491