1use rns_core::constants;
11use rns_core::destination::destination_hash;
12use rns_core::hash::truncated_hash;
13use rns_core::msgpack::{self, Value};
14use rns_core::transport::types::{InterfaceId, InterfaceInfo};
15use rns_core::transport::TransportEngine;
16
17use super::interface_stats::InterfaceStats;
18use super::time;
19
20pub trait InterfaceStatusView {
22 fn id(&self) -> InterfaceId;
23 fn info(&self) -> &InterfaceInfo;
24 fn online(&self) -> bool;
25 fn stats(&self) -> &InterfaceStats;
26}
27
28pub fn status_path_hash() -> [u8; 16] {
30 truncated_hash(b"/status")
31}
32
33pub fn path_path_hash() -> [u8; 16] {
35 truncated_hash(b"/path")
36}
37
38pub fn list_path_hash() -> [u8; 16] {
40 truncated_hash(b"/list")
41}
42
43pub fn is_management_path(path_hash: &[u8; 16]) -> bool {
45 *path_hash == status_path_hash()
46 || *path_hash == path_path_hash()
47 || *path_hash == list_path_hash()
48}
49
50pub fn management_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
54 destination_hash("rnstransport", &["remote", "management"], Some(transport_identity_hash))
55}
56
57pub fn blackhole_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
61 destination_hash("rnstransport", &["info", "blackhole"], Some(transport_identity_hash))
62}
63
64pub fn probe_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
68 destination_hash("rnstransport", &["probe"], Some(transport_identity_hash))
69}
70
71pub fn build_probe_announce(
75 identity: &rns_crypto::identity::Identity,
76 rng: &mut dyn rns_crypto::Rng,
77) -> Option<Vec<u8>> {
78 let identity_hash = *identity.hash();
79 let dest_hash = probe_dest_hash(&identity_hash);
80 let name_hash = rns_core::destination::name_hash("rnstransport", &["probe"]);
81 let mut random_hash = [0u8; 10];
82 rng.fill_bytes(&mut random_hash);
83
84 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
85 identity,
86 &dest_hash,
87 &name_hash,
88 &random_hash,
89 None,
90 None,
91 )
92 .ok()?;
93
94 let flags = rns_core::packet::PacketFlags {
95 header_type: constants::HEADER_1,
96 context_flag: constants::FLAG_UNSET,
97 transport_type: constants::TRANSPORT_BROADCAST,
98 destination_type: constants::DESTINATION_SINGLE,
99 packet_type: constants::PACKET_TYPE_ANNOUNCE,
100 };
101
102 let packet = rns_core::packet::RawPacket::pack(
103 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
104 )
105 .ok()?;
106
107 Some(packet.raw)
108}
109
110#[derive(Debug, Clone)]
112pub struct ManagementConfig {
113 pub enable_remote_management: bool,
115 pub remote_management_allowed: Vec<[u8; 16]>,
117 pub publish_blackhole: bool,
119}
120
121impl Default for ManagementConfig {
122 fn default() -> Self {
123 ManagementConfig {
124 enable_remote_management: false,
125 remote_management_allowed: Vec::new(),
126 publish_blackhole: false,
127 }
128 }
129}
130
131pub fn handle_status_request(
136 data: &[u8],
137 engine: &TransportEngine,
138 interfaces: &[&dyn InterfaceStatusView],
139 started: f64,
140 probe_responder_hash: Option<[u8; 16]>,
141) -> Option<Vec<u8>> {
142 let include_lstats = match msgpack::unpack_exact(data) {
144 Ok(Value::Array(arr)) if !arr.is_empty() => {
145 arr[0].as_bool().unwrap_or(false)
146 }
147 _ => false,
148 };
149
150 let mut iface_list = Vec::new();
152 let mut total_rxb: u64 = 0;
153 let mut total_txb: u64 = 0;
154
155 for entry in interfaces {
156 let id = entry.id();
157 let info = entry.info();
158 let stats = entry.stats();
159
160 total_rxb += stats.rxb;
161 total_txb += stats.txb;
162
163 let mut ifstats: Vec<(&str, Value)> = Vec::new();
164 ifstats.push(("name", Value::Str(info.name.clone())));
165 ifstats.push(("short_name", Value::Str(info.name.clone())));
166 ifstats.push(("status", Value::Bool(entry.online())));
167 ifstats.push(("mode", Value::UInt(info.mode as u64)));
168 ifstats.push(("rxb", Value::UInt(stats.rxb)));
169 ifstats.push(("txb", Value::UInt(stats.txb)));
170 if let Some(br) = info.bitrate {
171 ifstats.push(("bitrate", Value::UInt(br)));
172 } else {
173 ifstats.push(("bitrate", Value::Nil));
174 }
175 ifstats.push(("incoming_announce_freq", Value::Float(stats.incoming_announce_freq())));
176 ifstats.push(("outgoing_announce_freq", Value::Float(stats.outgoing_announce_freq())));
177 ifstats.push(("held_announces", Value::UInt(engine.held_announce_count(&id) as u64)));
178
179 ifstats.push(("ifac_signature", Value::Nil));
181 ifstats.push(("ifac_size", if info.bitrate.is_some() {
182 Value::UInt(0)
183 } else {
184 Value::Nil
185 }));
186 ifstats.push(("ifac_netname", Value::Nil));
187
188 ifstats.push(("clients", Value::Nil));
190 ifstats.push(("announce_queue", Value::Nil));
191 ifstats.push(("rxs", Value::UInt(0)));
192 ifstats.push(("txs", Value::UInt(0)));
193
194 let map = ifstats.into_iter()
196 .map(|(k, v)| (Value::Str(k.into()), v))
197 .collect();
198 iface_list.push(Value::Map(map));
199 }
200
201 let mut stats: Vec<(&str, Value)> = Vec::new();
203 stats.push(("interfaces", Value::Array(iface_list)));
204 stats.push(("rxb", Value::UInt(total_rxb)));
205 stats.push(("txb", Value::UInt(total_txb)));
206 stats.push(("rxs", Value::UInt(0)));
207 stats.push(("txs", Value::UInt(0)));
208
209 if let Some(identity_hash) = engine.config().identity_hash {
210 stats.push(("transport_id", Value::Bin(identity_hash.to_vec())));
211 stats.push(("transport_uptime", Value::Float(time::now() - started)));
212 }
213 stats.push(("probe_responder", match probe_responder_hash {
214 Some(hash) => Value::Bin(hash.to_vec()),
215 None => Value::Nil,
216 }));
217 stats.push(("rss", Value::Nil));
218
219 let stats_map = stats.into_iter()
220 .map(|(k, v)| (Value::Str(k.into()), v))
221 .collect();
222
223 let mut response = vec![Value::Map(stats_map)];
225 if include_lstats {
226 let link_count = engine.link_table_count();
227 response.push(Value::UInt(link_count as u64));
228 }
229
230 Some(msgpack::pack(&Value::Array(response)))
231}
232
233pub fn handle_path_request(
239 data: &[u8],
240 engine: &TransportEngine,
241) -> Option<Vec<u8>> {
242 let arr = match msgpack::unpack_exact(data) {
243 Ok(Value::Array(arr)) if !arr.is_empty() => arr,
244 _ => return None,
245 };
246
247 let command = match &arr[0] {
248 Value::Str(s) => s.as_str(),
249 _ => return None,
250 };
251
252 let dest_filter: Option<[u8; 16]> = if arr.len() > 1 {
253 match &arr[1] {
254 Value::Bin(b) if b.len() == 16 => {
255 let mut h = [0u8; 16];
256 h.copy_from_slice(b);
257 Some(h)
258 }
259 _ => None,
260 }
261 } else {
262 None
263 };
264
265 let max_hops: Option<u8> = if arr.len() > 2 {
266 arr[2].as_uint().map(|v| v as u8)
267 } else {
268 None
269 };
270
271 match command {
272 "table" => {
273 let paths = engine.get_path_table(max_hops);
274 let mut entries = Vec::new();
275 for p in &paths {
276 if let Some(ref filter) = dest_filter {
277 if p.0 != *filter {
278 continue;
279 }
280 }
281 let entry = vec![
283 (Value::Str("hash".into()), Value::Bin(p.0.to_vec())),
284 (Value::Str("timestamp".into()), Value::Float(p.1)),
285 (Value::Str("via".into()), Value::Bin(p.2.to_vec())),
286 (Value::Str("hops".into()), Value::UInt(p.3 as u64)),
287 (Value::Str("expires".into()), Value::Float(p.4)),
288 (Value::Str("interface".into()), Value::Str(p.5.clone())),
289 ];
290 entries.push(Value::Map(entry));
291 }
292 Some(msgpack::pack(&Value::Array(entries)))
293 }
294 "rates" => {
295 let rates = engine.get_rate_table();
296 let mut entries = Vec::new();
297 for r in &rates {
298 if let Some(ref filter) = dest_filter {
299 if r.0 != *filter {
300 continue;
301 }
302 }
303 let timestamps: Vec<Value> = r.4.iter().map(|t| Value::Float(*t)).collect();
305 let entry = vec![
306 (Value::Str("hash".into()), Value::Bin(r.0.to_vec())),
307 (Value::Str("last".into()), Value::Float(r.1)),
308 (Value::Str("rate_violations".into()), Value::UInt(r.2 as u64)),
309 (Value::Str("blocked_until".into()), Value::Float(r.3)),
310 (Value::Str("timestamps".into()), Value::Array(timestamps)),
311 ];
312 entries.push(Value::Map(entry));
313 }
314 Some(msgpack::pack(&Value::Array(entries)))
315 }
316 _ => None,
317 }
318}
319
320pub fn handle_blackhole_list_request(
324 engine: &TransportEngine,
325) -> Option<Vec<u8>> {
326 let blackholed = engine.get_blackholed();
327 let mut map_entries = Vec::new();
328 for (hash, created, expires, reason) in &blackholed {
329 let mut entry = vec![
330 (Value::Str("created".into()), Value::Float(*created)),
331 (Value::Str("expires".into()), Value::Float(*expires)),
332 ];
333 if let Some(r) = reason {
334 entry.push((Value::Str("reason".into()), Value::Str(r.clone())));
335 }
336 map_entries.push((Value::Bin(hash.to_vec()), Value::Map(entry)));
337 }
338 Some(msgpack::pack(&Value::Map(map_entries)))
339}
340
341pub fn build_management_announce(
345 identity: &rns_crypto::identity::Identity,
346 rng: &mut dyn rns_crypto::Rng,
347) -> Option<Vec<u8>> {
348 let identity_hash = *identity.hash();
349 let dest_hash = management_dest_hash(&identity_hash);
350 let name_hash = rns_core::destination::name_hash("rnstransport", &["remote", "management"]);
351 let mut random_hash = [0u8; 10];
352 rng.fill_bytes(&mut random_hash);
353
354 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
355 identity,
356 &dest_hash,
357 &name_hash,
358 &random_hash,
359 None, None, )
362 .ok()?;
363
364 let flags = rns_core::packet::PacketFlags {
365 header_type: constants::HEADER_1,
366 context_flag: constants::FLAG_UNSET,
367 transport_type: constants::TRANSPORT_BROADCAST,
368 destination_type: constants::DESTINATION_SINGLE,
369 packet_type: constants::PACKET_TYPE_ANNOUNCE,
370 };
371
372 let packet = rns_core::packet::RawPacket::pack(
373 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
374 )
375 .ok()?;
376
377 Some(packet.raw)
378}
379
380pub fn build_blackhole_announce(
384 identity: &rns_crypto::identity::Identity,
385 rng: &mut dyn rns_crypto::Rng,
386) -> Option<Vec<u8>> {
387 let identity_hash = *identity.hash();
388 let dest_hash = blackhole_dest_hash(&identity_hash);
389 let name_hash = rns_core::destination::name_hash("rnstransport", &["info", "blackhole"]);
390 let mut random_hash = [0u8; 10];
391 rng.fill_bytes(&mut random_hash);
392
393 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
394 identity,
395 &dest_hash,
396 &name_hash,
397 &random_hash,
398 None,
399 None,
400 )
401 .ok()?;
402
403 let flags = rns_core::packet::PacketFlags {
404 header_type: constants::HEADER_1,
405 context_flag: constants::FLAG_UNSET,
406 transport_type: constants::TRANSPORT_BROADCAST,
407 destination_type: constants::DESTINATION_SINGLE,
408 packet_type: constants::PACKET_TYPE_ANNOUNCE,
409 };
410
411 let packet = rns_core::packet::RawPacket::pack(
412 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
413 )
414 .ok()?;
415
416 Some(packet.raw)
417}