1use std::{io, net::SocketAddr, time::Duration};
2
3use socket2::Type as SockType;
4
5use crate::SocketFamily;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum UdpSocketType {
11 Dgram,
12 Raw,
13}
14
15impl UdpSocketType {
16 pub fn is_dgram(&self) -> bool {
18 matches!(self, UdpSocketType::Dgram)
19 }
20
21 pub fn is_raw(&self) -> bool {
23 matches!(self, UdpSocketType::Raw)
24 }
25
26 pub(crate) fn to_sock_type(self) -> SockType {
28 match self {
29 UdpSocketType::Dgram => SockType::DGRAM,
30 UdpSocketType::Raw => SockType::RAW,
31 }
32 }
33}
34
35#[derive(Debug, Clone)]
37#[non_exhaustive]
38pub struct UdpConfig {
39 pub socket_family: SocketFamily,
41 pub socket_type: UdpSocketType,
43 pub bind_addr: Option<SocketAddr>,
45 pub reuseaddr: Option<bool>,
47 pub reuseport: Option<bool>,
49 pub broadcast: Option<bool>,
51 pub ttl: Option<u32>,
53 pub hoplimit: Option<u32>,
55 pub read_timeout: Option<Duration>,
57 pub write_timeout: Option<Duration>,
59 pub recv_buffer_size: Option<usize>,
61 pub send_buffer_size: Option<usize>,
63 pub tos: Option<u32>,
65 pub tclass_v6: Option<u32>,
67 pub recv_pktinfo: Option<bool>,
69 pub only_v6: Option<bool>,
71 pub bind_device: Option<String>,
73}
74
75impl Default for UdpConfig {
76 fn default() -> Self {
77 Self {
78 socket_family: SocketFamily::IPV4,
79 socket_type: UdpSocketType::Dgram,
80 bind_addr: None,
81 reuseaddr: None,
82 reuseport: None,
83 broadcast: None,
84 ttl: None,
85 hoplimit: None,
86 read_timeout: None,
87 write_timeout: None,
88 recv_buffer_size: None,
89 send_buffer_size: None,
90 tos: None,
91 tclass_v6: None,
92 recv_pktinfo: None,
93 only_v6: None,
94 bind_device: None,
95 }
96 }
97}
98
99impl UdpConfig {
100 pub fn new() -> Self {
102 Self::default()
103 }
104
105 pub fn new_with_family(socket_family: SocketFamily) -> Self {
107 Self {
108 socket_family,
109 ..Self::default()
110 }
111 }
112
113 pub fn with_socket_family(mut self, socket_family: SocketFamily) -> Self {
115 self.socket_family = socket_family;
116 self
117 }
118
119 pub fn with_bind_addr(mut self, addr: SocketAddr) -> Self {
121 self.bind_addr = Some(addr);
122 self
123 }
124
125 pub fn with_bind(self, addr: SocketAddr) -> Self {
127 self.with_bind_addr(addr)
128 }
129
130 pub fn with_reuseaddr(mut self, on: bool) -> Self {
132 self.reuseaddr = Some(on);
133 self
134 }
135
136 pub fn with_reuseport(mut self, on: bool) -> Self {
138 self.reuseport = Some(on);
139 self
140 }
141
142 pub fn with_broadcast(mut self, on: bool) -> Self {
144 self.broadcast = Some(on);
145 self
146 }
147
148 pub fn with_ttl(mut self, ttl: u32) -> Self {
150 self.ttl = Some(ttl);
151 self
152 }
153
154 pub fn with_hoplimit(mut self, hops: u32) -> Self {
156 self.hoplimit = Some(hops);
157 self
158 }
159
160 pub fn with_hop_limit(self, hops: u32) -> Self {
162 self.with_hoplimit(hops)
163 }
164
165 pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
167 self.read_timeout = Some(timeout);
168 self
169 }
170
171 pub fn with_write_timeout(mut self, timeout: Duration) -> Self {
173 self.write_timeout = Some(timeout);
174 self
175 }
176
177 pub fn with_recv_buffer_size(mut self, size: usize) -> Self {
179 self.recv_buffer_size = Some(size);
180 self
181 }
182
183 pub fn with_send_buffer_size(mut self, size: usize) -> Self {
185 self.send_buffer_size = Some(size);
186 self
187 }
188
189 pub fn with_tos(mut self, tos: u32) -> Self {
191 self.tos = Some(tos);
192 self
193 }
194
195 pub fn with_tclass_v6(mut self, tclass: u32) -> Self {
197 self.tclass_v6 = Some(tclass);
198 self
199 }
200
201 pub fn with_recv_pktinfo(mut self, on: bool) -> Self {
203 self.recv_pktinfo = Some(on);
204 self
205 }
206
207 pub fn with_only_v6(mut self, only_v6: bool) -> Self {
209 self.only_v6 = Some(only_v6);
210 self
211 }
212
213 pub fn with_bind_device(mut self, iface: impl Into<String>) -> Self {
215 self.bind_device = Some(iface.into());
216 self
217 }
218
219 pub fn validate(&self) -> io::Result<()> {
221 if let Some(addr) = self.bind_addr {
222 let addr_family = crate::SocketFamily::from_socket_addr(&addr);
223 if addr_family != self.socket_family {
224 return Err(io::Error::new(
225 io::ErrorKind::InvalidInput,
226 "bind_addr family does not match socket_family",
227 ));
228 }
229 }
230
231 if self.socket_family.is_v4() {
232 if self.hoplimit.is_some() {
233 return Err(io::Error::new(
234 io::ErrorKind::InvalidInput,
235 "hoplimit is only supported for IPv6 UDP sockets",
236 ));
237 }
238 if self.tclass_v6.is_some() {
239 return Err(io::Error::new(
240 io::ErrorKind::InvalidInput,
241 "tclass_v6 is only supported for IPv6 UDP sockets",
242 ));
243 }
244 if self.only_v6.is_some() {
245 return Err(io::Error::new(
246 io::ErrorKind::InvalidInput,
247 "only_v6 is only supported for IPv6 UDP sockets",
248 ));
249 }
250 }
251
252 if self.socket_family.is_v6() {
253 if self.ttl.is_some() {
254 return Err(io::Error::new(
255 io::ErrorKind::InvalidInput,
256 "ttl is only supported for IPv4 UDP sockets",
257 ));
258 }
259 if self.broadcast.is_some() {
260 return Err(io::Error::new(
261 io::ErrorKind::InvalidInput,
262 "broadcast is only supported for IPv4 UDP sockets",
263 ));
264 }
265 }
266
267 if matches!(self.read_timeout, Some(timeout) if timeout.is_zero()) {
268 return Err(io::Error::new(
269 io::ErrorKind::InvalidInput,
270 "read_timeout must be greater than zero",
271 ));
272 }
273
274 if matches!(self.write_timeout, Some(timeout) if timeout.is_zero()) {
275 return Err(io::Error::new(
276 io::ErrorKind::InvalidInput,
277 "write_timeout must be greater than zero",
278 ));
279 }
280
281 if matches!(self.recv_buffer_size, Some(0)) {
282 return Err(io::Error::new(
283 io::ErrorKind::InvalidInput,
284 "recv_buffer_size must be greater than zero",
285 ));
286 }
287
288 if matches!(self.send_buffer_size, Some(0)) {
289 return Err(io::Error::new(
290 io::ErrorKind::InvalidInput,
291 "send_buffer_size must be greater than zero",
292 ));
293 }
294
295 if matches!(self.bind_device.as_deref(), Some("")) {
296 return Err(io::Error::new(
297 io::ErrorKind::InvalidInput,
298 "bind_device must not be empty",
299 ));
300 }
301
302 Ok(())
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn udp_config_default_values() {
312 let cfg = UdpConfig::default();
313 assert!(cfg.bind_addr.is_none());
314 assert!(cfg.reuseaddr.is_none());
315 assert!(cfg.reuseport.is_none());
316 assert!(cfg.broadcast.is_none());
317 assert!(cfg.ttl.is_none());
318 assert!(cfg.recv_buffer_size.is_none());
319 assert!(cfg.send_buffer_size.is_none());
320 assert!(cfg.tos.is_none());
321 assert!(cfg.tclass_v6.is_none());
322 assert!(cfg.recv_pktinfo.is_none());
323 assert!(cfg.only_v6.is_none());
324 assert!(cfg.bind_device.is_none());
325 }
326
327 #[test]
328 fn udp_config_with_family_builder() {
329 let cfg =
330 UdpConfig::new_with_family(SocketFamily::IPV6).with_bind("[::1]:0".parse().unwrap());
331 assert_eq!(cfg.socket_family, SocketFamily::IPV6);
332 assert!(cfg.bind_addr.is_some());
333 }
334
335 #[test]
336 fn udp_config_validate_rejects_ipv6_broadcast() {
337 let cfg = UdpConfig::new_with_family(SocketFamily::IPV6).with_broadcast(true);
338 assert!(cfg.validate().is_err());
339 }
340}