Skip to main content

timestamped_socket/
networkaddress.rs

1use 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    // Seal to ensure NetworkAddress can't be implemented outside our crate
18    pub trait SealedNA {}
19
20    // Seal to ensure MulticastJoinable can't be implemented outside our crate
21    pub trait SealedMC {}
22
23    // Token to ensure trait functions cannot be called outside our crate
24    pub struct PrivateToken;
25}
26
27/// A type of network address.
28pub 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
39/// A network address type which has joinable multicast addresses.
40pub 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        // Safety: the above assertions guarantee that alignment and size are correct.
76        // the resulting reference won't outlast the function, and result lives the entire
77        // duration of the function
78        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        // Safety: the above assertions guarantee that alignment and size are correct
107        // the resulting reference won't outlast the function, and addr lives the entire
108        // duration of the function
109        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        // Safety: the above assertions guarantee that alignment and size are correct.
147        // the resulting reference won't outlast the function, and result lives the entire
148        // duration of the function
149        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        // Safety: the above assertions guarantee that alignment and size are correct
180        // the resulting reference won't outlast the function, and addr lives the entire
181        // duration of the function
182        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}