1use std::io;
9use std::net::TcpStream;
10use std::time::Duration;
11
12use super::TcpSocket;
13use super::opt::{self, IPPROTO_TCP, Int, SO_KEEPALIVE, SOL_SOCKET};
14use crate::io::raw::Raw;
15use crate::io::{Handle, IoObject};
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
36pub struct TcpKeepalive {
37 pub time: Option<Duration>,
39 pub interval: Option<Duration>,
41 pub retries: Option<u32>,
43}
44
45#[cfg(any(
47 target_os = "linux",
48 target_os = "android",
49 target_os = "freebsd",
50 target_os = "netbsd"
51))]
52const KEEPALIVE: Option<[Int; 3]> =
53 Some([libc::TCP_KEEPIDLE, libc::TCP_KEEPINTVL, libc::TCP_KEEPCNT]);
54#[cfg(target_vendor = "apple")]
55const KEEPALIVE: Option<[Int; 3]> =
56 Some([libc::TCP_KEEPALIVE, libc::TCP_KEEPINTVL, libc::TCP_KEEPCNT]);
57#[cfg(windows)]
58const KEEPALIVE: Option<[Int; 3]> = {
59 use crate::syscore::windows as win;
60 Some([win::TCP_KEEPALIVE, win::TCP_KEEPINTVL, win::TCP_KEEPCNT])
61};
62#[cfg(not(any(
63 target_os = "linux",
64 target_os = "android",
65 target_os = "freebsd",
66 target_os = "netbsd",
67 target_vendor = "apple",
68 windows
69)))]
70const KEEPALIVE: Option<[Int; 3]> = None;
71
72#[cfg(all(
74 any(target_os = "linux", target_os = "android"),
75 not(any(target_arch = "sparc", target_arch = "sparc64"))
76))]
77const SO_INCOMING_CPU: Int = 49;
78#[cfg(all(
79 any(target_os = "linux", target_os = "android"),
80 any(target_arch = "sparc", target_arch = "sparc64")
81))]
82const SO_INCOMING_CPU: Int = 0x33;
83
84const SECOND: Duration = Duration::from_secs(1);
85
86fn secs(time: Duration, what: &str) -> io::Result<Int> {
88 opt::int(opt::whole(time, SECOND), what)
89}
90
91fn set_keepalive_timing(raw: Raw, keepalive: TcpKeepalive) -> io::Result<()> {
93 let [idle, interval, count] =
94 KEEPALIVE.ok_or_else(|| opt::unsupported("TCP keepalive timing"))?;
95 if let Some(time) = keepalive.time {
96 opt::set(raw, IPPROTO_TCP, idle, secs(time, "keepalive time")?)?;
97 }
98 if let Some(time) = keepalive.interval {
99 opt::set(
100 raw,
101 IPPROTO_TCP,
102 interval,
103 secs(time, "keepalive interval")?,
104 )?;
105 }
106 if let Some(retries) = keepalive.retries {
107 opt::set_count(raw, IPPROTO_TCP, count, retries, "keepalive retry count")?;
108 }
109 opt::set_flag(raw, SOL_SOCKET, SO_KEEPALIVE, true)
110}
111
112fn keepalive_timing(raw: Raw) -> io::Result<TcpKeepalive> {
114 let [idle, interval, count] =
115 KEEPALIVE.ok_or_else(|| opt::unsupported("TCP keepalive timing"))?;
116 let secs = |name| opt::count(raw, IPPROTO_TCP, name).map(|s| Duration::from_secs(s.into()));
117 Ok(TcpKeepalive {
118 time: Some(secs(idle)?),
119 interval: Some(secs(interval)?),
120 retries: Some(opt::count(raw, IPPROTO_TCP, count)?),
121 })
122}
123
124fn set_user_timeout(raw: Raw, timeout: Option<Duration>) -> io::Result<()> {
127 #[cfg(any(target_os = "linux", target_os = "android"))]
128 {
129 let ms = timeout.map_or(0, |t| opt::whole(t, Duration::from_millis(1)));
130 opt::set(
131 raw,
132 IPPROTO_TCP,
133 libc::TCP_USER_TIMEOUT,
134 opt::int(ms, "user timeout")?,
135 )
136 }
137 #[cfg(not(any(target_os = "linux", target_os = "android")))]
138 {
139 let _ = (raw, timeout);
140 Err(opt::unsupported("TCP_USER_TIMEOUT"))
141 }
142}
143
144fn user_timeout(raw: Raw) -> io::Result<Option<Duration>> {
146 #[cfg(any(target_os = "linux", target_os = "android"))]
147 {
148 let ms = opt::count(raw, IPPROTO_TCP, libc::TCP_USER_TIMEOUT)?;
149 Ok((ms > 0).then(|| Duration::from_millis(ms.into())))
150 }
151 #[cfg(not(any(target_os = "linux", target_os = "android")))]
152 {
153 let _ = raw;
154 Err(opt::unsupported("TCP_USER_TIMEOUT"))
155 }
156}
157
158macro_rules! tcp_options {
160 ($($ty:ty => $raw:ident),* $(,)?) => {$(
161 impl $ty {
162 pub fn set_keepalive_timing(&self, keepalive: TcpKeepalive) -> io::Result<()> {
171 set_keepalive_timing(self.$raw(), keepalive)
172 }
173
174 pub fn keepalive_timing(&self) -> io::Result<TcpKeepalive> {
180 keepalive_timing(self.$raw())
181 }
182
183 pub fn set_user_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
191 set_user_timeout(self.$raw(), timeout)
192 }
193
194 pub fn user_timeout(&self) -> io::Result<Option<Duration>> {
200 user_timeout(self.$raw())
201 }
202 }
203 )*};
204}
205
206tcp_options!(TcpSocket => raw, Handle<TcpStream> => raw_io);
207
208impl TcpSocket {
209 pub fn set_incoming_cpu(&self, cpu: u32) -> io::Result<()> {
217 #[cfg(any(target_os = "linux", target_os = "android"))]
218 return opt::set_count(self.raw(), SOL_SOCKET, SO_INCOMING_CPU, cpu, "CPU");
219 #[cfg(not(any(target_os = "linux", target_os = "android")))]
220 {
221 let _ = cpu;
222 Err(opt::unsupported("SO_INCOMING_CPU"))
223 }
224 }
225
226 pub fn incoming_cpu(&self) -> io::Result<Option<u32>> {
233 #[cfg(any(target_os = "linux", target_os = "android"))]
234 return opt::get::<Int>(self.raw(), SOL_SOCKET, SO_INCOMING_CPU)
235 .map(|cpu| u32::try_from(cpu).ok());
236 #[cfg(not(any(target_os = "linux", target_os = "android")))]
237 Err(opt::unsupported("SO_INCOMING_CPU"))
238 }
239
240 pub fn set_fastopen(&self, queue: u32) -> io::Result<()> {
249 #[cfg(any(target_os = "linux", target_os = "android"))]
250 return opt::set_count(
251 self.raw(),
252 IPPROTO_TCP,
253 libc::TCP_FASTOPEN,
254 queue,
255 "fast open queue",
256 );
257 #[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
258 return opt::set_flag(self.raw(), IPPROTO_TCP, libc::TCP_FASTOPEN, queue > 0);
259 #[cfg(windows)]
260 return opt::set_flag(
261 self.raw(),
262 IPPROTO_TCP,
263 crate::syscore::windows::TCP_FASTOPEN,
264 queue > 0,
265 );
266 #[cfg(not(any(
267 target_os = "linux",
268 target_os = "android",
269 target_vendor = "apple",
270 target_os = "freebsd",
271 windows
272 )))]
273 {
274 let _ = queue;
275 Err(opt::unsupported("TCP_FASTOPEN"))
276 }
277 }
278
279 pub fn fastopen(&self) -> io::Result<u32> {
286 #[cfg(any(target_os = "linux", target_os = "android"))]
287 return opt::count(self.raw(), IPPROTO_TCP, libc::TCP_FASTOPEN);
288 #[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
289 return opt::flag(self.raw(), IPPROTO_TCP, libc::TCP_FASTOPEN).map(u32::from);
290 #[cfg(windows)]
291 return opt::flag(
292 self.raw(),
293 IPPROTO_TCP,
294 crate::syscore::windows::TCP_FASTOPEN,
295 )
296 .map(u32::from);
297 #[cfg(not(any(
298 target_os = "linux",
299 target_os = "android",
300 target_vendor = "apple",
301 target_os = "freebsd",
302 windows
303 )))]
304 Err(opt::unsupported("TCP_FASTOPEN"))
305 }
306
307 pub fn set_defer_accept(&self, wait: Option<Duration>) -> io::Result<()> {
315 #[cfg(any(target_os = "linux", target_os = "android"))]
316 {
317 let secs = match wait {
318 Some(wait) => secs(wait, "deferred accept's wait")?,
319 None => 0,
320 };
321 opt::set(self.raw(), IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, secs)
322 }
323 #[cfg(not(any(target_os = "linux", target_os = "android")))]
324 {
325 let _ = wait;
326 Err(opt::unsupported("TCP_DEFER_ACCEPT"))
327 }
328 }
329
330 pub fn defer_accept(&self) -> io::Result<Option<Duration>> {
337 #[cfg(any(target_os = "linux", target_os = "android"))]
338 {
339 let secs = opt::count(self.raw(), IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?;
340 Ok((secs > 0).then(|| Duration::from_secs(secs.into())))
341 }
342 #[cfg(not(any(target_os = "linux", target_os = "android")))]
343 Err(opt::unsupported("TCP_DEFER_ACCEPT"))
344 }
345}
346
347impl Handle<TcpStream> {
348 pub fn set_quickack(&self, quickack: bool) -> io::Result<()> {
356 #[cfg(any(target_os = "linux", target_os = "android"))]
357 return opt::set_flag(self.raw_io(), IPPROTO_TCP, libc::TCP_QUICKACK, quickack);
358 #[cfg(not(any(target_os = "linux", target_os = "android")))]
359 {
360 let _ = quickack;
361 Err(opt::unsupported("TCP_QUICKACK"))
362 }
363 }
364
365 pub fn quickack(&self) -> io::Result<bool> {
371 #[cfg(any(target_os = "linux", target_os = "android"))]
372 return opt::flag(self.raw_io(), IPPROTO_TCP, libc::TCP_QUICKACK);
373 #[cfg(not(any(target_os = "linux", target_os = "android")))]
374 Err(opt::unsupported("TCP_QUICKACK"))
375 }
376}