timestamped_socket/
networkaddress.rs1use std::{
2 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
3 os::fd::RawFd,
4};
5
6use crate::{control_message::zeroed_sockaddr_storage, interface::InterfaceName};
7
8use self::sealed::{PrivateToken, SealedMC, SealedNA};
9
10#[cfg(target_os = "linux")]
11pub use self::linux::*;
12
13#[cfg(target_os = "linux")]
14mod linux;
15
16pub(crate) mod sealed {
17 pub trait SealedNA {}
19
20 pub trait SealedMC {}
22
23 pub struct PrivateToken;
25}
26
27pub trait NetworkAddress: Copy + Sized + SealedNA {
29 #[doc(hidden)]
30 fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage;
31 #[doc(hidden)]
32 fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self>;
33 #[doc(hidden)]
34 fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self>;
35 #[doc(hidden)]
36 fn port(&self) -> u16;
37}
38
39pub trait MulticastJoinable: NetworkAddress + SealedMC {
41 #[doc(hidden)]
42 fn join_multicast(
43 &self,
44 socket: RawFd,
45 interface: InterfaceName,
46 _token: PrivateToken,
47 ) -> std::io::Result<()>;
48 #[doc(hidden)]
49 fn leave_multicast(
50 &self,
51 socket: RawFd,
52 interface: InterfaceName,
53 _token: PrivateToken,
54 ) -> std::io::Result<()>;
55}
56
57impl SealedNA for SocketAddrV4 {}
58
59impl NetworkAddress for SocketAddrV4 {
60 #[expect(
61 clippy::cast_possible_truncation,
62 reason = "libc constants have inconsistent sizes."
63 )]
64 fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
65 const _: () = assert!(
66 std::mem::size_of::<libc::sockaddr_storage>()
67 >= std::mem::size_of::<libc::sockaddr_in>()
68 );
69 const _: () = assert!(
70 std::mem::align_of::<libc::sockaddr_storage>()
71 >= std::mem::align_of::<libc::sockaddr_in>()
72 );
73
74 let mut result = zeroed_sockaddr_storage();
75 let out = unsafe { &mut (*(&raw mut result).cast::<libc::sockaddr_in>()) };
79 out.sin_family = libc::AF_INET as _;
80 out.sin_port = u16::from_ne_bytes(self.port().to_be_bytes());
81 out.sin_addr = libc::in_addr {
82 s_addr: u32::from_ne_bytes(self.ip().octets()),
83 };
84
85 result
86 }
87
88 #[expect(
89 clippy::cast_possible_truncation,
90 reason = "libc constants have inconsistent sizes."
91 )]
92 fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
93 const _: () = assert!(
94 std::mem::size_of::<libc::sockaddr_storage>()
95 >= std::mem::size_of::<libc::sockaddr_in>()
96 );
97 const _: () = assert!(
98 std::mem::align_of::<libc::sockaddr_storage>()
99 >= std::mem::align_of::<libc::sockaddr_in>()
100 );
101
102 if addr.ss_family != libc::AF_INET as _ {
103 return None;
104 }
105
106 let input = unsafe { &(*(&raw const addr).cast::<libc::sockaddr_in>()) };
110 Some(SocketAddrV4::new(
111 Ipv4Addr::from(input.sin_addr.s_addr.to_ne_bytes()),
112 u16::from_be_bytes(input.sin_port.to_ne_bytes()),
113 ))
114 }
115
116 fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
117 match addr {
118 IpAddr::V4(addr) => Some(SocketAddrV4::new(addr, port)),
119 IpAddr::V6(_) => None,
120 }
121 }
122
123 fn port(&self) -> u16 {
124 self.port()
125 }
126}
127
128impl SealedNA for SocketAddrV6 {}
129
130impl NetworkAddress for SocketAddrV6 {
131 #[expect(
132 clippy::cast_possible_truncation,
133 reason = "libc constants have inconsistent sizes."
134 )]
135 fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
136 const _: () = assert!(
137 std::mem::size_of::<libc::sockaddr_storage>()
138 >= std::mem::size_of::<libc::sockaddr_in6>()
139 );
140 const _: () = assert!(
141 std::mem::align_of::<libc::sockaddr_storage>()
142 >= std::mem::align_of::<libc::sockaddr_in6>()
143 );
144
145 let mut result = zeroed_sockaddr_storage();
146 let out = unsafe { &mut (*(&raw mut result).cast::<libc::sockaddr_in6>()) };
150 out.sin6_family = libc::AF_INET6 as _;
151 out.sin6_port = u16::from_ne_bytes(self.port().to_be_bytes());
152 out.sin6_addr = libc::in6_addr {
153 s6_addr: self.ip().octets(),
154 };
155 out.sin6_flowinfo = self.flowinfo();
156 out.sin6_scope_id = self.scope_id();
157
158 result
159 }
160
161 #[expect(
162 clippy::cast_possible_truncation,
163 reason = "libc constants have inconsistent sizes."
164 )]
165 fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
166 const _: () = assert!(
167 std::mem::size_of::<libc::sockaddr_storage>()
168 >= std::mem::size_of::<libc::sockaddr_in6>()
169 );
170 const _: () = assert!(
171 std::mem::align_of::<libc::sockaddr_storage>()
172 >= std::mem::align_of::<libc::sockaddr_in6>()
173 );
174
175 if addr.ss_family != libc::AF_INET6 as _ {
176 return None;
177 }
178
179 let input = unsafe { &(*(&raw const addr).cast::<libc::sockaddr_in6>()) };
183 Some(SocketAddrV6::new(
184 Ipv6Addr::from(input.sin6_addr.s6_addr),
185 u16::from_be_bytes(input.sin6_port.to_ne_bytes()),
186 input.sin6_flowinfo,
187 input.sin6_scope_id,
188 ))
189 }
190
191 fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
192 match addr {
193 IpAddr::V4(_) => None,
194 IpAddr::V6(addr) => Some(SocketAddrV6::new(addr, port, 0, 0)),
195 }
196 }
197
198 fn port(&self) -> u16 {
199 self.port()
200 }
201}
202
203impl SealedNA for SocketAddr {}
204
205impl NetworkAddress for SocketAddr {
206 fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
207 match self {
208 SocketAddr::V4(addr) => addr.to_sockaddr(PrivateToken),
209 SocketAddr::V6(addr) => addr.to_sockaddr(PrivateToken),
210 }
211 }
212
213 fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
214 match addr.ss_family.into() {
215 libc::AF_INET => Some(SocketAddr::V4(SocketAddrV4::from_sockaddr(
216 addr,
217 PrivateToken,
218 )?)),
219 libc::AF_INET6 => Some(SocketAddr::V6(SocketAddrV6::from_sockaddr(
220 addr,
221 PrivateToken,
222 )?)),
223 _ => None,
224 }
225 }
226
227 fn from_ip_and_port(addr: IpAddr, port: u16) -> Option<Self> {
228 Some(SocketAddr::new(addr, port))
229 }
230
231 fn port(&self) -> u16 {
232 self.port()
233 }
234}