Skip to main content

net_io/
opts.rs

1//! Socket options: before bind, per accepted or dialed connection, and live.
2
3use std::time::Duration;
4
5use crate::error::NetError;
6use crate::sys::{self, AsSock};
7
8/// Keepalive probes: idle time before the first, interval between them, and count.
9///
10/// ```
11/// let k = net_io::Keepalive::DEFAULT;
12/// assert_eq!(k.count, 6);
13/// ```
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct Keepalive {
16    /// Idle time before the first probe.
17    pub idle: Duration,
18    /// Time between probes.
19    pub interval: Duration,
20    /// Probes before the connection is dropped.
21    pub count: u32,
22}
23
24impl Keepalive {
25    /// 60 s, 10 s, 6.
26    pub const DEFAULT: Keepalive = Keepalive {
27        idle: Duration::from_secs(60),
28        interval: Duration::from_secs(10),
29        count: 6,
30    };
31}
32
33/// What happens to unsent data at close.
34///
35/// ```
36/// assert_ne!(net_io::Linger::Off, net_io::Linger::Abortive);
37/// ```
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum Linger {
40    /// The OS default: close returns at once, the kernel sends what it can.
41    Off,
42    /// RST on close; kernel resources are freed at once.
43    Abortive,
44    /// Close waits up to this long for unsent data.
45    Timeout(Duration),
46}
47
48impl Linger {
49    fn to_sys(self) -> Option<Duration> {
50        match self {
51            Linger::Off => None,
52            Linger::Abortive => Some(Duration::ZERO),
53            Linger::Timeout(d) => Some(d),
54        }
55    }
56}
57
58/// Options applied to each accepted or dialed TCP connection. `None` leaves the OS default.
59///
60/// ```
61/// use net_io::{Keepalive, TcpOptions};
62/// let opts = TcpOptions::new().nodelay(true).keepalive(Keepalive::DEFAULT);
63/// assert_eq!(opts.nodelay, Some(true));
64/// ```
65#[must_use]
66#[non_exhaustive]
67#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
68pub struct TcpOptions {
69    /// `TCP_NODELAY`.
70    pub nodelay: Option<bool>,
71    /// Keepalive probes.
72    pub keepalive: Option<Keepalive>,
73    /// Linger at close.
74    pub linger: Option<Linger>,
75    /// `SO_SNDBUF`.
76    pub send_buffer: Option<u32>,
77    /// `SO_RCVBUF`.
78    pub receive_buffer: Option<u32>,
79    /// Hop limit (`IP_TTL` / `IPV6_UNICAST_HOPS`).
80    pub ttl: Option<u8>,
81}
82
83impl TcpOptions {
84    /// Every option at the OS default.
85    pub fn new() -> TcpOptions {
86        TcpOptions::default()
87    }
88
89    /// `TCP_NODELAY`.
90    pub fn nodelay(mut self, on: bool) -> TcpOptions {
91        self.nodelay = Some(on);
92        self
93    }
94
95    /// Keepalive probes.
96    pub fn keepalive(mut self, k: Keepalive) -> TcpOptions {
97        self.keepalive = Some(k);
98        self
99    }
100
101    /// Linger at close.
102    pub fn linger(mut self, l: Linger) -> TcpOptions {
103        self.linger = Some(l);
104        self
105    }
106
107    /// `SO_SNDBUF`.
108    pub fn send_buffer(mut self, bytes: u32) -> TcpOptions {
109        self.send_buffer = Some(bytes);
110        self
111    }
112
113    /// `SO_RCVBUF`.
114    pub fn receive_buffer(mut self, bytes: u32) -> TcpOptions {
115        self.receive_buffer = Some(bytes);
116        self
117    }
118
119    /// Hop limit.
120    pub fn ttl(mut self, hops: u8) -> TcpOptions {
121        self.ttl = Some(hops);
122        self
123    }
124
125    /// Applies the options that are set to `stream`.
126    pub(crate) fn apply<S: AsSock>(&self, socket: &S, v6: bool) -> Result<(), NetError> {
127        let sock = socket.sock();
128        let os = |op: &'static str| move |e| NetError::from_os(op, e);
129        if let Some(on) = self.nodelay {
130            sys::set_nodelay(sock, on).map_err(os("TCP_NODELAY"))?;
131        }
132        if let Some(k) = self.keepalive {
133            check_keepalive(&k)?;
134            sys::set_keepalive(sock, Some((k.idle, k.interval, k.count)))
135                .map_err(os("keepalive"))?;
136        }
137        if let Some(l) = self.linger {
138            sys::set_linger(sock, l.to_sys()).map_err(os("SO_LINGER"))?;
139        }
140        if let Some(n) = self.send_buffer {
141            sys::set_send_buffer(sock, n).map_err(os("SO_SNDBUF"))?;
142        }
143        if let Some(n) = self.receive_buffer {
144            sys::set_receive_buffer(sock, n).map_err(os("SO_RCVBUF"))?;
145        }
146        if let Some(h) = self.ttl {
147            sys::set_ttl(sock, v6, h).map_err(os("IP_TTL"))?;
148        }
149        Ok(())
150    }
151
152    /// Whether any option is set.
153    pub(crate) fn is_empty(&self) -> bool {
154        *self == TcpOptions::default()
155    }
156}
157
158fn check_keepalive(k: &Keepalive) -> Result<(), NetError> {
159    if cfg!(windows) && k.count > 255 {
160        return Err(NetError::InvalidConfig {
161            field: "keepalive",
162            reason: crate::error::ConfigReason::TooLarge { max: 255 },
163        });
164    }
165    if k.idle.is_zero() || k.interval.is_zero() || k.count == 0 {
166        return Err(NetError::InvalidConfig {
167            field: "keepalive",
168            reason: crate::error::ConfigReason::Zero,
169        });
170    }
171    Ok(())
172}
173
174/// Options set on a listening socket before `bind` and `listen`.
175///
176/// ```
177/// use net_io::{ListenOptions, TcpListener};
178/// let options = ListenOptions::new().backlog(4096).reuse_port(cfg!(target_os = "linux"));
179/// let listener = TcpListener::bind_with("127.0.0.1:0", options)?;
180/// println!("effective backlog {:?}", listener.info().backlog);
181/// # Ok::<(), net_io::NetError>(())
182/// ```
183#[must_use]
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
185pub struct ListenOptions {
186    pub(crate) backlog: u32,
187    pub(crate) reuse_address: bool,
188    pub(crate) reuse_port: bool,
189    pub(crate) ipv6_only: bool,
190    pub(crate) send_buffer: Option<u32>,
191    pub(crate) receive_buffer: Option<u32>,
192    pub(crate) tcp: TcpOptions,
193}
194
195impl Default for ListenOptions {
196    fn default() -> ListenOptions {
197        ListenOptions {
198            backlog: 1024,
199            reuse_address: cfg!(unix),
200            reuse_port: false,
201            ipv6_only: true,
202            send_buffer: None,
203            receive_buffer: None,
204            tcp: TcpOptions::new().nodelay(true),
205        }
206    }
207}
208
209impl ListenOptions {
210    /// Backlog 1,024; reuse address on Unix, exclusive address use on Windows;
211    /// IPv6-only; `TCP_NODELAY` on accepted sockets.
212    pub fn new() -> ListenOptions {
213        ListenOptions::default()
214    }
215
216    /// The listen backlog (capped by the OS; the effective value is in `ListenerInfo`).
217    pub fn backlog(mut self, n: u32) -> ListenOptions {
218        self.backlog = n;
219        self
220    }
221
222    /// Unix: `SO_REUSEADDR`. Windows: `false` = `SO_EXCLUSIVEADDRUSE`, `true` = `SO_REUSEADDR`.
223    pub fn reuse_address(mut self, on: bool) -> ListenOptions {
224        self.reuse_address = on;
225        self
226    }
227
228    /// `SO_REUSEPORT` (`Unsupported` on Windows).
229    pub fn reuse_port(mut self, on: bool) -> ListenOptions {
230        self.reuse_port = on;
231        self
232    }
233
234    /// `IPV6_V6ONLY` for IPv6 addresses (on by default; decision D-026).
235    pub fn ipv6_only(mut self, on: bool) -> ListenOptions {
236        self.ipv6_only = on;
237        self
238    }
239
240    /// `SO_SNDBUF` on the listener (before `listen`).
241    pub fn send_buffer(mut self, bytes: u32) -> ListenOptions {
242        self.send_buffer = Some(bytes);
243        self
244    }
245
246    /// `SO_RCVBUF` on the listener (before `listen`, so window scaling is negotiated).
247    pub fn receive_buffer(mut self, bytes: u32) -> ListenOptions {
248        self.receive_buffer = Some(bytes);
249        self
250    }
251
252    /// Options applied to every accepted socket.
253    pub fn tcp_options(mut self, options: TcpOptions) -> ListenOptions {
254        self.tcp = options;
255        self
256    }
257
258    pub(crate) fn params(&self) -> sys::ListenParams {
259        sys::ListenParams {
260            reuse_address: self.reuse_address,
261            reuse_port: self.reuse_port,
262            ipv6_only: self.ipv6_only,
263            send_buffer: self.send_buffer,
264            receive_buffer: self.receive_buffer,
265            backlog: self.backlog,
266        }
267    }
268}
269
270/// Live get and set on an open socket.
271///
272/// ```no_run
273/// use net_io::{Keepalive, Linger};
274/// let conn = net_io::connect("127.0.0.1:7000")?;
275/// conn.options().set_keepalive(Some(Keepalive::DEFAULT))?;
276/// conn.options().set_linger(Linger::Abortive)?;
277/// let effective = conn.options().set_receive_buffer(256 * 1024)?;
278/// println!("receive buffer is {effective} bytes");
279/// # Ok::<(), net_io::NetError>(())
280/// ```
281#[derive(Debug)]
282pub struct SocketOptions<'a> {
283    pub(crate) sock: sys::Sock<'a>,
284    pub(crate) v6: bool,
285}
286
287fn os(op: &'static str) -> impl Fn(std::io::Error) -> NetError {
288    move |e| NetError::from_os(op, e)
289}
290
291impl SocketOptions<'_> {
292    /// `TCP_NODELAY`.
293    ///
294    /// # Errors
295    ///
296    /// `Os` when the OS call fails.
297    pub fn nodelay(&self) -> Result<bool, NetError> {
298        sys::nodelay(self.sock).map_err(os("TCP_NODELAY"))
299    }
300
301    /// Sets `TCP_NODELAY`.
302    ///
303    /// # Errors
304    ///
305    /// `Os` when the OS call fails.
306    pub fn set_nodelay(&self, on: bool) -> Result<(), NetError> {
307        sys::set_nodelay(self.sock, on).map_err(os("TCP_NODELAY"))
308    }
309
310    /// Keepalive probes (`None` = off).
311    ///
312    /// # Errors
313    ///
314    /// `Os` when the OS call fails.
315    pub fn keepalive(&self) -> Result<Option<Keepalive>, NetError> {
316        Ok(sys::keepalive(self.sock)
317            .map_err(os("keepalive"))?
318            .map(|(idle, interval, count)| Keepalive {
319                idle,
320                interval,
321                count,
322            }))
323    }
324
325    /// Sets keepalive probes (`None` = off).
326    ///
327    /// # Errors
328    ///
329    /// `InvalidConfig` for a zero value or a count above 255 on Windows; `Os`.
330    pub fn set_keepalive(&self, k: Option<Keepalive>) -> Result<(), NetError> {
331        if let Some(k) = &k {
332            check_keepalive(k)?;
333        }
334        sys::set_keepalive(self.sock, k.map(|k| (k.idle, k.interval, k.count)))
335            .map_err(os("keepalive"))
336    }
337
338    /// Linger at close.
339    ///
340    /// # Errors
341    ///
342    /// `Os` when the OS call fails.
343    pub fn linger(&self) -> Result<Linger, NetError> {
344        Ok(match sys::linger(self.sock).map_err(os("SO_LINGER"))? {
345            None => Linger::Off,
346            Some(d) if d.is_zero() => Linger::Abortive,
347            Some(d) => Linger::Timeout(d),
348        })
349    }
350
351    /// Sets linger at close.
352    ///
353    /// # Errors
354    ///
355    /// `Os` when the OS call fails.
356    pub fn set_linger(&self, l: Linger) -> Result<(), NetError> {
357        sys::set_linger(self.sock, l.to_sys()).map_err(os("SO_LINGER"))
358    }
359
360    /// The effective `SO_SNDBUF`.
361    ///
362    /// # Errors
363    ///
364    /// `Os` when the OS call fails.
365    pub fn send_buffer(&self) -> Result<u32, NetError> {
366        sys::send_buffer(self.sock).map_err(os("SO_SNDBUF"))
367    }
368
369    /// Sets `SO_SNDBUF` and returns the effective value (Linux doubles it).
370    ///
371    /// # Errors
372    ///
373    /// `Os` when the OS call fails.
374    pub fn set_send_buffer(&self, bytes: u32) -> Result<u32, NetError> {
375        sys::set_send_buffer(self.sock, bytes).map_err(os("SO_SNDBUF"))
376    }
377
378    /// The effective `SO_RCVBUF`.
379    ///
380    /// # Errors
381    ///
382    /// `Os` when the OS call fails.
383    pub fn receive_buffer(&self) -> Result<u32, NetError> {
384        sys::receive_buffer(self.sock).map_err(os("SO_RCVBUF"))
385    }
386
387    /// Sets `SO_RCVBUF` and returns the effective value (Linux doubles it).
388    ///
389    /// # Errors
390    ///
391    /// `Os` when the OS call fails.
392    pub fn set_receive_buffer(&self, bytes: u32) -> Result<u32, NetError> {
393        sys::set_receive_buffer(self.sock, bytes).map_err(os("SO_RCVBUF"))
394    }
395
396    /// The hop limit.
397    ///
398    /// # Errors
399    ///
400    /// `Os` when the OS call fails.
401    pub fn ttl(&self) -> Result<u8, NetError> {
402        sys::ttl(self.sock, self.v6).map_err(os("IP_TTL"))
403    }
404
405    /// Sets the hop limit.
406    ///
407    /// # Errors
408    ///
409    /// `Os` when the OS call fails.
410    pub fn set_ttl(&self, hops: u8) -> Result<(), NetError> {
411        sys::set_ttl(self.sock, self.v6, hops).map_err(os("IP_TTL"))
412    }
413}
414
415/// Options for a Unix socket listener. The defaults: mode `0600` (only the
416/// owner may connect, decision D-024), stale socket files removed, backlog 1,024.
417///
418/// ```
419/// let options = net_io::LocalOptions::new().group_access(33).require_uid(1000);
420/// # let _ = options;
421/// ```
422#[must_use]
423#[derive(Clone, Debug, PartialEq, Eq)]
424pub struct LocalOptions {
425    pub(crate) mode: u32,
426    pub(crate) owner: Option<u32>,
427    pub(crate) group: Option<u32>,
428    pub(crate) remove_stale: bool,
429    pub(crate) require_uid: Option<u32>,
430    pub(crate) backlog: u32,
431}
432
433impl Default for LocalOptions {
434    fn default() -> LocalOptions {
435        LocalOptions::new()
436    }
437}
438
439impl LocalOptions {
440    /// Mode `0600`, stale socket files removed, backlog 1,024.
441    pub fn new() -> LocalOptions {
442        LocalOptions {
443            mode: 0o600,
444            owner: None,
445            group: None,
446            remove_stale: true,
447            require_uid: None,
448            backlog: 1024,
449        }
450    }
451
452    /// The socket file's permission bits (only the low 9 bits are used).
453    pub fn mode(mut self, mode: u32) -> LocalOptions {
454        self.mode = mode & 0o777;
455        self
456    }
457
458    /// The socket file's owner (needs the privilege to change owners).
459    pub fn owner(mut self, uid: u32) -> LocalOptions {
460        self.owner = Some(uid);
461        self
462    }
463
464    /// The socket file's group; the mode is unchanged.
465    pub fn group(mut self, gid: u32) -> LocalOptions {
466        self.group = Some(gid);
467        self
468    }
469
470    /// The socket file's group and mode `0660`: members of `gid` may connect.
471    pub fn group_access(mut self, gid: u32) -> LocalOptions {
472        self.group = Some(gid);
473        self.mode = 0o660;
474        self
475    }
476
477    /// Whether a socket file nobody listens on is removed before binding
478    /// (default on). A live listener's file, or anything that is not a socket,
479    /// is never removed.
480    pub fn remove_stale(mut self, on: bool) -> LocalOptions {
481        self.remove_stale = on;
482        self
483    }
484
485    /// Refuses peers whose user id is not `uid`, at accept, before any read.
486    pub fn require_uid(mut self, uid: u32) -> LocalOptions {
487        self.require_uid = Some(uid);
488        self
489    }
490
491    /// The listen backlog (capped by the OS).
492    pub fn backlog(mut self, backlog: u32) -> LocalOptions {
493        self.backlog = backlog;
494        self
495    }
496}
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501
502    #[test]
503    fn test_local_options_builders() {
504        let o = LocalOptions::new();
505        assert_eq!((o.mode, o.remove_stale, o.backlog), (0o600, true, 1024));
506        let o = o
507            .group_access(7)
508            .owner(3)
509            .require_uid(9)
510            .mode(0o1777)
511            .backlog(5);
512        assert_eq!(o.mode, 0o777);
513        assert_eq!(
514            (o.group, o.owner, o.require_uid),
515            (Some(7), Some(3), Some(9))
516        );
517        assert_eq!(LocalOptions::default(), LocalOptions::new());
518    }
519
520    #[test]
521    fn test_keepalive_zero_rejected() {
522        let k = Keepalive {
523            idle: Duration::ZERO,
524            interval: Duration::from_secs(1),
525            count: 1,
526        };
527        assert!(check_keepalive(&k).is_err());
528        assert!(check_keepalive(&Keepalive::DEFAULT).is_ok());
529    }
530
531    #[test]
532    fn test_listen_defaults() {
533        let o = ListenOptions::new();
534        assert_eq!(o.backlog, 1024);
535        assert!(o.ipv6_only);
536        assert_eq!(o.reuse_address, cfg!(unix));
537        assert_eq!(o.tcp.nodelay, Some(true));
538    }
539
540    #[test]
541    fn test_tcp_options_is_empty() {
542        assert!(TcpOptions::new().is_empty());
543        assert!(!TcpOptions::new().ttl(5).is_empty());
544    }
545}