irontide_tracker/
compact.rs1use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
2
3use crate::error::{Error, Result};
4
5pub fn parse_compact_peers(data: &[u8]) -> Result<Vec<SocketAddr>> {
11 if !data.len().is_multiple_of(6) {
12 return Err(Error::InvalidResponse(format!(
13 "compact peers length {} is not a multiple of 6",
14 data.len()
15 )));
16 }
17
18 let mut peers = Vec::with_capacity(data.len() / 6);
19 for chunk in data.chunks_exact(6) {
20 let ip = Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]);
21 let port = u16::from_be_bytes([chunk[4], chunk[5]]);
22 peers.push(SocketAddr::V4(SocketAddrV4::new(ip, port)));
23 }
24 Ok(peers)
25}
26
27#[must_use]
31pub fn encode_compact_peers(peers: &[SocketAddr]) -> Vec<u8> {
32 let mut buf = Vec::with_capacity(peers.len() * 6);
33 for peer in peers {
34 if let SocketAddr::V4(v4) = peer {
35 buf.extend_from_slice(&v4.ip().octets());
36 buf.extend_from_slice(&v4.port().to_be_bytes());
37 }
38 }
39 buf
40}
41
42pub fn parse_compact_peers6(data: &[u8]) -> Result<Vec<SocketAddr>> {
48 if !data.len().is_multiple_of(18) {
49 return Err(Error::InvalidResponse(format!(
50 "compact peers6 length {} is not a multiple of 18",
51 data.len()
52 )));
53 }
54
55 let mut peers = Vec::with_capacity(data.len() / 18);
56 for chunk in data.chunks_exact(18) {
57 let ip = Ipv6Addr::from(<[u8; 16]>::try_from(&chunk[..16]).unwrap());
58 let port = u16::from_be_bytes([chunk[16], chunk[17]]);
59 peers.push(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)));
60 }
61 Ok(peers)
62}
63
64#[must_use]
68pub fn encode_compact_peers6(peers: &[SocketAddr]) -> Vec<u8> {
69 let mut buf = Vec::with_capacity(peers.len() * 18);
70 for peer in peers {
71 if let SocketAddr::V6(v6) = peer {
72 buf.extend_from_slice(&v6.ip().octets());
73 buf.extend_from_slice(&v6.port().to_be_bytes());
74 }
75 }
76 buf
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn parse_single_peer() {
85 let data = [192, 168, 1, 1, 0x1A, 0xE1];
87 let peers = parse_compact_peers(&data).unwrap();
88 assert_eq!(peers.len(), 1);
89 assert_eq!(peers[0].to_string(), "192.168.1.1:6881");
90 }
91
92 #[test]
93 fn parse_multiple_peers() {
94 let mut data = Vec::new();
95 data.extend_from_slice(&[10, 0, 0, 1, 0x1F, 0x90]);
97 data.extend_from_slice(&[172, 16, 0, 1, 0x1A, 0xE1]);
99 let peers = parse_compact_peers(&data).unwrap();
100 assert_eq!(peers.len(), 2);
101 assert_eq!(peers[0].to_string(), "10.0.0.1:8080");
102 assert_eq!(peers[1].to_string(), "172.16.0.1:6881");
103 }
104
105 #[test]
106 fn parse_empty() {
107 let peers = parse_compact_peers(&[]).unwrap();
108 assert!(peers.is_empty());
109 }
110
111 #[test]
112 fn reject_invalid_length() {
113 assert!(parse_compact_peers(&[1, 2, 3, 4, 5]).is_err());
114 }
115
116 #[test]
119 fn encode_compact_peers_round_trip() {
120 let peers: Vec<SocketAddr> = vec![
121 "192.168.1.1:6881".parse().unwrap(),
122 "10.0.0.1:8080".parse().unwrap(),
123 ];
124 let encoded = encode_compact_peers(&peers);
125 assert_eq!(encoded.len(), 12);
126 let decoded = parse_compact_peers(&encoded).unwrap();
127 assert_eq!(peers, decoded);
128 }
129
130 #[test]
131 fn encode_compact_peers_skips_ipv6() {
132 let peers: Vec<SocketAddr> = vec![
133 "192.168.1.1:6881".parse().unwrap(),
134 "[::1]:6881".parse().unwrap(),
135 ];
136 let encoded = encode_compact_peers(&peers);
137 assert_eq!(encoded.len(), 6); }
139
140 #[test]
143 fn parse_single_peer6() {
144 let mut data = [0u8; 18];
146 data[15] = 1; data[16] = 0x1A;
148 data[17] = 0xE1; let peers = parse_compact_peers6(&data).unwrap();
150 assert_eq!(peers.len(), 1);
151 assert_eq!(peers[0], "[::1]:6881".parse::<SocketAddr>().unwrap());
152 }
153
154 #[test]
155 fn parse_multiple_peers6() {
156 let mut data = Vec::new();
157 let ip1: Ipv6Addr = "2001:db8::1".parse().unwrap();
159 data.extend_from_slice(&ip1.octets());
160 data.extend_from_slice(&8080u16.to_be_bytes());
161 let ip2: Ipv6Addr = "fe80::42".parse().unwrap();
163 data.extend_from_slice(&ip2.octets());
164 data.extend_from_slice(&6881u16.to_be_bytes());
165
166 let peers = parse_compact_peers6(&data).unwrap();
167 assert_eq!(peers.len(), 2);
168 assert_eq!(
169 peers[0],
170 "[2001:db8::1]:8080".parse::<SocketAddr>().unwrap()
171 );
172 assert_eq!(peers[1], "[fe80::42]:6881".parse::<SocketAddr>().unwrap());
173 }
174
175 #[test]
176 fn parse_empty_peers6() {
177 let peers = parse_compact_peers6(&[]).unwrap();
178 assert!(peers.is_empty());
179 }
180
181 #[test]
182 fn reject_invalid_length_peers6() {
183 assert!(parse_compact_peers6(&[0u8; 17]).is_err());
184 assert!(parse_compact_peers6(&[0u8; 19]).is_err());
185 }
186
187 #[test]
190 fn encode_compact_peers6_round_trip() {
191 let peers: Vec<SocketAddr> = vec![
192 "[2001:db8::1]:8080".parse().unwrap(),
193 "[::1]:6881".parse().unwrap(),
194 ];
195 let encoded = encode_compact_peers6(&peers);
196 assert_eq!(encoded.len(), 36);
197 let decoded = parse_compact_peers6(&encoded).unwrap();
198 assert_eq!(peers, decoded);
199 }
200
201 #[test]
202 fn encode_compact_peers6_skips_ipv4() {
203 let peers: Vec<SocketAddr> = vec![
204 "[::1]:6881".parse().unwrap(),
205 "192.168.1.1:6881".parse().unwrap(),
206 ];
207 let encoded = encode_compact_peers6(&peers);
208 assert_eq!(encoded.len(), 18); }
210}