Skip to main content

net_io/
opts.rs

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