1use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
2
3use irontide_core::Id20;
4
5use crate::error::{Error, Result};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct CompactNodeInfo {
12 pub id: Id20,
14 pub addr: SocketAddr,
16}
17
18pub const COMPACT_NODE_SIZE: usize = 26;
20
21impl CompactNodeInfo {
22 #[must_use]
24 pub fn to_bytes(&self) -> [u8; COMPACT_NODE_SIZE] {
25 let mut buf = [0u8; COMPACT_NODE_SIZE];
26 buf[..20].copy_from_slice(self.id.as_bytes());
27 match self.addr {
28 SocketAddr::V4(v4) => {
29 buf[20..24].copy_from_slice(&v4.ip().octets());
30 buf[24..26].copy_from_slice(&v4.port().to_be_bytes());
31 }
32 SocketAddr::V6(_) => {
33 }
35 }
36 buf
37 }
38
39 pub fn from_bytes(data: &[u8]) -> Result<Self> {
45 if data.len() != COMPACT_NODE_SIZE {
46 return Err(Error::InvalidCompactNode(format!(
47 "expected {COMPACT_NODE_SIZE} bytes, got {}",
48 data.len()
49 )));
50 }
51 let id =
52 Id20::from_bytes(&data[..20]).map_err(|e| Error::InvalidCompactNode(e.to_string()))?;
53 let ip = Ipv4Addr::new(data[20], data[21], data[22], data[23]);
54 let port = u16::from_be_bytes([data[24], data[25]]);
55 Ok(Self {
56 id,
57 addr: SocketAddr::V4(SocketAddrV4::new(ip, port)),
58 })
59 }
60}
61
62pub fn parse_compact_nodes(data: &[u8]) -> Result<Vec<CompactNodeInfo>> {
68 if !data.len().is_multiple_of(COMPACT_NODE_SIZE) {
69 return Err(Error::InvalidCompactNode(format!(
70 "compact nodes length {} is not a multiple of {COMPACT_NODE_SIZE}",
71 data.len()
72 )));
73 }
74 let mut nodes = Vec::with_capacity(data.len() / COMPACT_NODE_SIZE);
75 for chunk in data.chunks_exact(COMPACT_NODE_SIZE) {
76 nodes.push(CompactNodeInfo::from_bytes(chunk)?);
77 }
78 Ok(nodes)
79}
80
81#[must_use]
83pub fn encode_compact_nodes(nodes: &[CompactNodeInfo]) -> Vec<u8> {
84 let mut buf = Vec::with_capacity(nodes.len() * COMPACT_NODE_SIZE);
85 for node in nodes {
86 buf.extend_from_slice(&node.to_bytes());
87 }
88 buf
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub struct CompactNodeInfo6 {
96 pub id: Id20,
98 pub addr: SocketAddr,
100}
101
102pub const COMPACT_NODE6_SIZE: usize = 38;
104
105impl CompactNodeInfo6 {
106 #[must_use]
108 pub fn to_bytes(&self) -> [u8; COMPACT_NODE6_SIZE] {
109 let mut buf = [0u8; COMPACT_NODE6_SIZE];
110 buf[..20].copy_from_slice(self.id.as_bytes());
111 match self.addr {
112 SocketAddr::V6(v6) => {
113 buf[20..36].copy_from_slice(&v6.ip().octets());
114 buf[36..38].copy_from_slice(&v6.port().to_be_bytes());
115 }
116 SocketAddr::V4(_) => {
117 }
119 }
120 buf
121 }
122
123 pub fn from_bytes(data: &[u8]) -> Result<Self> {
129 if data.len() != COMPACT_NODE6_SIZE {
130 return Err(Error::InvalidCompactNode(format!(
131 "expected {COMPACT_NODE6_SIZE} bytes, got {}",
132 data.len()
133 )));
134 }
135 let id =
136 Id20::from_bytes(&data[..20]).map_err(|e| Error::InvalidCompactNode(e.to_string()))?;
137 let ip = Ipv6Addr::from(<[u8; 16]>::try_from(&data[20..36]).unwrap());
138 let port = u16::from_be_bytes([data[36], data[37]]);
139 Ok(Self {
140 id,
141 addr: SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)),
142 })
143 }
144}
145
146pub fn parse_compact_nodes6(data: &[u8]) -> Result<Vec<CompactNodeInfo6>> {
152 if !data.len().is_multiple_of(COMPACT_NODE6_SIZE) {
153 return Err(Error::InvalidCompactNode(format!(
154 "compact nodes6 length {} is not a multiple of {COMPACT_NODE6_SIZE}",
155 data.len()
156 )));
157 }
158 let mut nodes = Vec::with_capacity(data.len() / COMPACT_NODE6_SIZE);
159 for chunk in data.chunks_exact(COMPACT_NODE6_SIZE) {
160 nodes.push(CompactNodeInfo6::from_bytes(chunk)?);
161 }
162 Ok(nodes)
163}
164
165#[must_use]
167pub fn encode_compact_nodes6(nodes: &[CompactNodeInfo6]) -> Vec<u8> {
168 let mut buf = Vec::with_capacity(nodes.len() * COMPACT_NODE6_SIZE);
169 for node in nodes {
170 buf.extend_from_slice(&node.to_bytes());
171 }
172 buf
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 fn sample_node() -> CompactNodeInfo {
180 CompactNodeInfo {
181 id: Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap(),
182 addr: "192.168.1.1:6881".parse().unwrap(),
183 }
184 }
185
186 #[test]
187 fn round_trip_single() {
188 let node = sample_node();
189 let bytes = node.to_bytes();
190 assert_eq!(bytes.len(), 26);
191 let decoded = CompactNodeInfo::from_bytes(&bytes).unwrap();
192 assert_eq!(node, decoded);
193 }
194
195 #[test]
196 fn round_trip_multiple() {
197 let nodes = vec![
198 sample_node(),
199 CompactNodeInfo {
200 id: Id20::ZERO,
201 addr: "10.0.0.1:8080".parse().unwrap(),
202 },
203 ];
204 let encoded = encode_compact_nodes(&nodes);
205 assert_eq!(encoded.len(), 52);
206 let decoded = parse_compact_nodes(&encoded).unwrap();
207 assert_eq!(nodes, decoded);
208 }
209
210 #[test]
211 fn boundary_ip_and_port() {
212 let node = CompactNodeInfo {
213 id: Id20::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap(),
214 addr: "255.255.255.255:65535".parse().unwrap(),
215 };
216 let bytes = node.to_bytes();
217 let decoded = CompactNodeInfo::from_bytes(&bytes).unwrap();
218 assert_eq!(node, decoded);
219 }
220
221 #[test]
222 fn reject_wrong_length() {
223 assert!(CompactNodeInfo::from_bytes(&[0u8; 25]).is_err());
224 assert!(parse_compact_nodes(&[0u8; 27]).is_err());
225 }
226
227 #[test]
228 fn empty_compact_nodes() {
229 let nodes = parse_compact_nodes(&[]).unwrap();
230 assert!(nodes.is_empty());
231 }
232
233 fn sample_node6() -> CompactNodeInfo6 {
236 CompactNodeInfo6 {
237 id: Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap(),
238 addr: "[2001:db8::1]:6881".parse().unwrap(),
239 }
240 }
241
242 #[test]
243 fn round_trip_single_v6() {
244 let node = sample_node6();
245 let bytes = node.to_bytes();
246 assert_eq!(bytes.len(), 38);
247 let decoded = CompactNodeInfo6::from_bytes(&bytes).unwrap();
248 assert_eq!(node, decoded);
249 }
250
251 #[test]
252 fn round_trip_multiple_v6() {
253 let nodes = vec![
254 sample_node6(),
255 CompactNodeInfo6 {
256 id: Id20::ZERO,
257 addr: "[::1]:8080".parse().unwrap(),
258 },
259 ];
260 let encoded = encode_compact_nodes6(&nodes);
261 assert_eq!(encoded.len(), 76);
262 let decoded = parse_compact_nodes6(&encoded).unwrap();
263 assert_eq!(nodes, decoded);
264 }
265
266 #[test]
267 fn boundary_v6() {
268 let node = CompactNodeInfo6 {
269 id: Id20::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap(),
270 addr: "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535"
271 .parse()
272 .unwrap(),
273 };
274 let bytes = node.to_bytes();
275 let decoded = CompactNodeInfo6::from_bytes(&bytes).unwrap();
276 assert_eq!(node, decoded);
277 }
278
279 #[test]
280 fn reject_wrong_length_v6() {
281 assert!(CompactNodeInfo6::from_bytes(&[0u8; 37]).is_err());
282 assert!(parse_compact_nodes6(&[0u8; 39]).is_err());
283 }
284
285 #[test]
286 fn empty_compact_nodes6() {
287 let nodes = parse_compact_nodes6(&[]).unwrap();
288 assert!(nodes.is_empty());
289 }
290}