1use socket2::Type as SockType;
2use std::io;
3use std::net::SocketAddr;
4use std::time::Duration;
5
6use crate::SocketFamily;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum TcpSocketType {
12 Stream,
13 Raw,
14}
15
16impl TcpSocketType {
17 pub fn is_stream(&self) -> bool {
19 matches!(self, TcpSocketType::Stream)
20 }
21
22 pub fn is_raw(&self) -> bool {
24 matches!(self, TcpSocketType::Raw)
25 }
26
27 pub(crate) fn to_sock_type(self) -> SockType {
29 match self {
30 TcpSocketType::Stream => SockType::STREAM,
31 TcpSocketType::Raw => SockType::RAW,
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
38#[non_exhaustive]
39pub struct TcpConfig {
40 pub socket_family: SocketFamily,
42 pub socket_type: TcpSocketType,
44 pub bind_addr: Option<SocketAddr>,
46 pub nonblocking: bool,
48 pub reuseaddr: Option<bool>,
50 pub reuseport: Option<bool>,
52 pub nodelay: Option<bool>,
54 pub linger: Option<Duration>,
56 pub ttl: Option<u32>,
58 pub hoplimit: Option<u32>,
60 pub read_timeout: Option<Duration>,
62 pub write_timeout: Option<Duration>,
64 pub recv_buffer_size: Option<usize>,
66 pub send_buffer_size: Option<usize>,
68 pub tos: Option<u32>,
70 pub tclass_v6: Option<u32>,
72 pub only_v6: Option<bool>,
74 pub bind_device: Option<String>,
76 pub keepalive: Option<bool>,
78}
79
80impl TcpConfig {
81 pub fn new(socket_family: SocketFamily) -> Self {
83 match socket_family {
84 SocketFamily::IPV4 => Self::v4_stream(),
85 SocketFamily::IPV6 => Self::v6_stream(),
86 }
87 }
88
89 pub fn v4_stream() -> Self {
91 Self {
92 socket_family: SocketFamily::IPV4,
93 socket_type: TcpSocketType::Stream,
94 bind_addr: None,
95 nonblocking: false,
96 reuseaddr: None,
97 reuseport: None,
98 nodelay: None,
99 linger: None,
100 ttl: None,
101 hoplimit: None,
102 read_timeout: None,
103 write_timeout: None,
104 recv_buffer_size: None,
105 send_buffer_size: None,
106 tos: None,
107 tclass_v6: None,
108 only_v6: None,
109 bind_device: None,
110 keepalive: None,
111 }
112 }
113
114 pub fn raw_v4() -> Self {
116 Self {
117 socket_family: SocketFamily::IPV4,
118 socket_type: TcpSocketType::Raw,
119 ..Self::v4_stream()
120 }
121 }
122
123 pub fn v6_stream() -> Self {
125 Self {
126 socket_family: SocketFamily::IPV6,
127 socket_type: TcpSocketType::Stream,
128 ..Self::v4_stream()
129 }
130 }
131
132 pub fn raw_v6() -> Self {
134 Self {
135 socket_family: SocketFamily::IPV6,
136 socket_type: TcpSocketType::Raw,
137 ..Self::v4_stream()
138 }
139 }
140
141 pub fn with_bind(mut self, addr: SocketAddr) -> Self {
144 self.bind_addr = Some(addr);
145 self
146 }
147
148 pub fn with_bind_addr(self, addr: SocketAddr) -> Self {
149 self.with_bind(addr)
150 }
151
152 pub fn with_nonblocking(mut self, flag: bool) -> Self {
153 self.nonblocking = flag;
154 self
155 }
156
157 pub fn with_reuseaddr(mut self, flag: bool) -> Self {
158 self.reuseaddr = Some(flag);
159 self
160 }
161
162 pub fn with_reuseport(mut self, flag: bool) -> Self {
163 self.reuseport = Some(flag);
164 self
165 }
166
167 pub fn with_nodelay(mut self, flag: bool) -> Self {
168 self.nodelay = Some(flag);
169 self
170 }
171
172 pub fn with_linger(mut self, dur: Duration) -> Self {
173 self.linger = Some(dur);
174 self
175 }
176
177 pub fn with_ttl(mut self, ttl: u32) -> Self {
178 self.ttl = Some(ttl);
179 self
180 }
181
182 pub fn with_hoplimit(mut self, hops: u32) -> Self {
183 self.hoplimit = Some(hops);
184 self
185 }
186
187 pub fn with_hop_limit(self, hops: u32) -> Self {
188 self.with_hoplimit(hops)
189 }
190
191 pub fn with_keepalive(mut self, on: bool) -> Self {
192 self.keepalive = Some(on);
193 self
194 }
195
196 pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
197 self.read_timeout = Some(timeout);
198 self
199 }
200
201 pub fn with_write_timeout(mut self, timeout: Duration) -> Self {
202 self.write_timeout = Some(timeout);
203 self
204 }
205
206 pub fn with_recv_buffer_size(mut self, size: usize) -> Self {
207 self.recv_buffer_size = Some(size);
208 self
209 }
210
211 pub fn with_send_buffer_size(mut self, size: usize) -> Self {
212 self.send_buffer_size = Some(size);
213 self
214 }
215
216 pub fn with_tos(mut self, tos: u32) -> Self {
217 self.tos = Some(tos);
218 self
219 }
220
221 pub fn with_tclass_v6(mut self, tclass: u32) -> Self {
222 self.tclass_v6 = Some(tclass);
223 self
224 }
225
226 pub fn with_only_v6(mut self, only_v6: bool) -> Self {
227 self.only_v6 = Some(only_v6);
228 self
229 }
230
231 pub fn with_bind_device(mut self, iface: impl Into<String>) -> Self {
232 self.bind_device = Some(iface.into());
233 self
234 }
235
236 pub fn validate(&self) -> io::Result<()> {
238 if let Some(addr) = self.bind_addr {
239 let addr_family = crate::SocketFamily::from_socket_addr(&addr);
240 if addr_family != self.socket_family {
241 return Err(io::Error::new(
242 io::ErrorKind::InvalidInput,
243 "bind_addr family does not match socket_family",
244 ));
245 }
246 }
247
248 if self.socket_family.is_v4() {
249 if self.hoplimit.is_some() {
250 return Err(io::Error::new(
251 io::ErrorKind::InvalidInput,
252 "hoplimit is only supported for IPv6 TCP sockets",
253 ));
254 }
255 if self.tclass_v6.is_some() {
256 return Err(io::Error::new(
257 io::ErrorKind::InvalidInput,
258 "tclass_v6 is only supported for IPv6 TCP sockets",
259 ));
260 }
261 if self.only_v6.is_some() {
262 return Err(io::Error::new(
263 io::ErrorKind::InvalidInput,
264 "only_v6 is only supported for IPv6 TCP sockets",
265 ));
266 }
267 }
268
269 if self.socket_family.is_v6() && self.ttl.is_some() {
270 return Err(io::Error::new(
271 io::ErrorKind::InvalidInput,
272 "ttl is only supported for IPv4 TCP sockets",
273 ));
274 }
275
276 if matches!(self.read_timeout, Some(timeout) if timeout.is_zero()) {
277 return Err(io::Error::new(
278 io::ErrorKind::InvalidInput,
279 "read_timeout must be greater than zero",
280 ));
281 }
282
283 if matches!(self.write_timeout, Some(timeout) if timeout.is_zero()) {
284 return Err(io::Error::new(
285 io::ErrorKind::InvalidInput,
286 "write_timeout must be greater than zero",
287 ));
288 }
289
290 if matches!(self.recv_buffer_size, Some(0)) {
291 return Err(io::Error::new(
292 io::ErrorKind::InvalidInput,
293 "recv_buffer_size must be greater than zero",
294 ));
295 }
296
297 if matches!(self.send_buffer_size, Some(0)) {
298 return Err(io::Error::new(
299 io::ErrorKind::InvalidInput,
300 "send_buffer_size must be greater than zero",
301 ));
302 }
303
304 if matches!(self.bind_device.as_deref(), Some("")) {
305 return Err(io::Error::new(
306 io::ErrorKind::InvalidInput,
307 "bind_device must not be empty",
308 ));
309 }
310
311 Ok(())
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn tcp_config_builders() {
321 let addr: SocketAddr = "127.0.0.1:80".parse().unwrap();
322 let cfg = TcpConfig::new(SocketFamily::IPV4)
323 .with_bind_addr(addr)
324 .with_nonblocking(true)
325 .with_reuseaddr(true)
326 .with_reuseport(true)
327 .with_nodelay(true)
328 .with_ttl(10)
329 .with_recv_buffer_size(8192)
330 .with_send_buffer_size(8192)
331 .with_tos(0x10)
332 .with_tclass_v6(0x20);
333
334 assert_eq!(cfg.socket_family, SocketFamily::IPV4);
335 assert_eq!(cfg.socket_type, TcpSocketType::Stream);
336 assert_eq!(cfg.bind_addr, Some(addr));
337 assert!(cfg.nonblocking);
338 assert_eq!(cfg.reuseaddr, Some(true));
339 assert_eq!(cfg.reuseport, Some(true));
340 assert_eq!(cfg.nodelay, Some(true));
341 assert_eq!(cfg.ttl, Some(10));
342 assert_eq!(cfg.recv_buffer_size, Some(8192));
343 assert_eq!(cfg.send_buffer_size, Some(8192));
344 assert_eq!(cfg.tos, Some(0x10));
345 assert_eq!(cfg.tclass_v6, Some(0x20));
346 }
347
348 #[test]
349 fn new_with_ipv6_family_creates_v6_stream() {
350 let cfg = TcpConfig::new(SocketFamily::IPV6);
351 assert_eq!(cfg.socket_family, SocketFamily::IPV6);
352 assert_eq!(cfg.socket_type, TcpSocketType::Stream);
353 }
354
355 #[test]
356 fn tcp_config_validate_rejects_family_mismatch() {
357 let cfg = TcpConfig::v4_stream().with_bind("[::1]:0".parse().unwrap());
358 assert!(cfg.validate().is_err());
359 }
360}