use rns_core::constants;
use rns_core::destination::destination_hash;
use rns_core::hash::truncated_hash;
use rns_core::msgpack::{self, Value};
use rns_core::transport::types::{InterfaceId, InterfaceInfo};
use rns_core::transport::TransportEngine;
use super::interface_stats::InterfaceStats;
use super::time;
pub trait InterfaceStatusView {
fn id(&self) -> InterfaceId;
fn info(&self) -> &InterfaceInfo;
fn online(&self) -> bool;
fn stats(&self) -> &InterfaceStats;
}
pub fn status_path_hash() -> [u8; 16] {
truncated_hash(b"/status")
}
pub fn path_path_hash() -> [u8; 16] {
truncated_hash(b"/path")
}
pub fn list_path_hash() -> [u8; 16] {
truncated_hash(b"/list")
}
pub fn is_management_path(path_hash: &[u8; 16]) -> bool {
*path_hash == status_path_hash()
|| *path_hash == path_path_hash()
|| *path_hash == list_path_hash()
}
pub fn management_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
destination_hash(
"rnstransport",
&["remote", "management"],
Some(transport_identity_hash),
)
}
pub fn blackhole_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
destination_hash(
"rnstransport",
&["info", "blackhole"],
Some(transport_identity_hash),
)
}
pub fn probe_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
destination_hash("rnstransport", &["probe"], Some(transport_identity_hash))
}
pub fn build_probe_announce(
identity: &rns_crypto::identity::Identity,
rng: &mut dyn rns_crypto::Rng,
) -> Option<Vec<u8>> {
let identity_hash = *identity.hash();
let dest_hash = probe_dest_hash(&identity_hash);
let name_hash = rns_core::destination::name_hash("rnstransport", &["probe"]);
let mut random_hash = [0u8; 10];
rng.fill_bytes(&mut random_hash);
let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
identity,
&dest_hash,
&name_hash,
&random_hash,
None,
None,
)
.ok()?;
let flags = rns_core::packet::PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_ANNOUNCE,
};
let packet = rns_core::packet::RawPacket::pack(
flags,
0,
&dest_hash,
None,
constants::CONTEXT_NONE,
&announce_data,
)
.ok()?;
Some(packet.raw)
}
#[derive(Debug, Clone)]
pub struct ManagementConfig {
pub enable_remote_management: bool,
pub remote_management_allowed: Vec<[u8; 16]>,
pub publish_blackhole: bool,
}
impl Default for ManagementConfig {
fn default() -> Self {
ManagementConfig {
enable_remote_management: false,
remote_management_allowed: Vec::new(),
publish_blackhole: false,
}
}
}
pub fn handle_status_request(
data: &[u8],
engine: &TransportEngine,
interfaces: &[&dyn InterfaceStatusView],
started: f64,
probe_responder_hash: Option<[u8; 16]>,
) -> Option<Vec<u8>> {
let include_lstats = match msgpack::unpack_exact(data) {
Ok(Value::Array(arr)) if !arr.is_empty() => arr[0].as_bool().unwrap_or(false),
_ => false,
};
let mut iface_list = Vec::new();
let mut total_rxb: u64 = 0;
let mut total_txb: u64 = 0;
for entry in interfaces {
let id = entry.id();
let info = entry.info();
let stats = entry.stats();
total_rxb += stats.rxb;
total_txb += stats.txb;
let mut ifstats: Vec<(&str, Value)> = Vec::new();
ifstats.push(("name", Value::Str(info.name.clone())));
ifstats.push(("short_name", Value::Str(info.name.clone())));
ifstats.push(("status", Value::Bool(entry.online())));
ifstats.push(("mode", Value::UInt(info.mode as u64)));
ifstats.push(("rxb", Value::UInt(stats.rxb)));
ifstats.push(("txb", Value::UInt(stats.txb)));
if let Some(br) = info.bitrate {
ifstats.push(("bitrate", Value::UInt(br)));
} else {
ifstats.push(("bitrate", Value::Nil));
}
ifstats.push((
"incoming_announce_freq",
Value::Float(stats.incoming_announce_freq()),
));
ifstats.push((
"outgoing_announce_freq",
Value::Float(stats.outgoing_announce_freq()),
));
ifstats.push((
"held_announces",
Value::UInt(engine.held_announce_count(&id) as u64),
));
ifstats.push(("ifac_signature", Value::Nil));
ifstats.push((
"ifac_size",
if info.bitrate.is_some() {
Value::UInt(0)
} else {
Value::Nil
},
));
ifstats.push(("ifac_netname", Value::Nil));
ifstats.push(("clients", Value::Nil));
ifstats.push(("announce_queue", Value::Nil));
ifstats.push(("rxs", Value::UInt(0)));
ifstats.push(("txs", Value::UInt(0)));
let map = ifstats
.into_iter()
.map(|(k, v)| (Value::Str(k.into()), v))
.collect();
iface_list.push(Value::Map(map));
}
let mut stats: Vec<(&str, Value)> = Vec::new();
stats.push(("interfaces", Value::Array(iface_list)));
stats.push(("rxb", Value::UInt(total_rxb)));
stats.push(("txb", Value::UInt(total_txb)));
stats.push(("rxs", Value::UInt(0)));
stats.push(("txs", Value::UInt(0)));
if let Some(identity_hash) = engine.config().identity_hash {
stats.push(("transport_id", Value::Bin(identity_hash.to_vec())));
stats.push(("transport_uptime", Value::Float(time::now() - started)));
}
stats.push((
"probe_responder",
match probe_responder_hash {
Some(hash) => Value::Bin(hash.to_vec()),
None => Value::Nil,
},
));
stats.push(("rss", Value::Nil));
let stats_map = stats
.into_iter()
.map(|(k, v)| (Value::Str(k.into()), v))
.collect();
let mut response = vec![Value::Map(stats_map)];
if include_lstats {
let link_count = engine.link_table_count();
response.push(Value::UInt(link_count as u64));
}
Some(msgpack::pack(&Value::Array(response)))
}
pub fn handle_path_request(data: &[u8], engine: &TransportEngine) -> Option<Vec<u8>> {
let arr = match msgpack::unpack_exact(data) {
Ok(Value::Array(arr)) if !arr.is_empty() => arr,
_ => return None,
};
let command = match &arr[0] {
Value::Str(s) => s.as_str(),
_ => return None,
};
let dest_filter: Option<[u8; 16]> = if arr.len() > 1 {
match &arr[1] {
Value::Bin(b) if b.len() == 16 => {
let mut h = [0u8; 16];
h.copy_from_slice(b);
Some(h)
}
_ => None,
}
} else {
None
};
let max_hops: Option<u8> = if arr.len() > 2 {
arr[2].as_uint().map(|v| v as u8)
} else {
None
};
match command {
"table" => {
let paths = engine.get_path_table(max_hops);
let mut entries = Vec::new();
for p in &paths {
if let Some(ref filter) = dest_filter {
if p.0 != *filter {
continue;
}
}
let entry = vec![
(Value::Str("hash".into()), Value::Bin(p.0.to_vec())),
(Value::Str("timestamp".into()), Value::Float(p.1)),
(Value::Str("via".into()), Value::Bin(p.2.to_vec())),
(Value::Str("hops".into()), Value::UInt(p.3 as u64)),
(Value::Str("expires".into()), Value::Float(p.4)),
(Value::Str("interface".into()), Value::Str(p.5.clone())),
];
entries.push(Value::Map(entry));
}
Some(msgpack::pack(&Value::Array(entries)))
}
"rates" => {
let rates = engine.get_rate_table();
let mut entries = Vec::new();
for r in &rates {
if let Some(ref filter) = dest_filter {
if r.0 != *filter {
continue;
}
}
let timestamps: Vec<Value> = r.4.iter().map(|t| Value::Float(*t)).collect();
let entry = vec![
(Value::Str("hash".into()), Value::Bin(r.0.to_vec())),
(Value::Str("last".into()), Value::Float(r.1)),
(
Value::Str("rate_violations".into()),
Value::UInt(r.2 as u64),
),
(Value::Str("blocked_until".into()), Value::Float(r.3)),
(Value::Str("timestamps".into()), Value::Array(timestamps)),
];
entries.push(Value::Map(entry));
}
Some(msgpack::pack(&Value::Array(entries)))
}
_ => None,
}
}
pub fn handle_blackhole_list_request(engine: &TransportEngine) -> Option<Vec<u8>> {
let blackholed = engine.get_blackholed();
let mut map_entries = Vec::new();
for (hash, created, expires, reason) in &blackholed {
let mut entry = vec![
(Value::Str("created".into()), Value::Float(*created)),
(Value::Str("expires".into()), Value::Float(*expires)),
];
if let Some(r) = reason {
entry.push((Value::Str("reason".into()), Value::Str(r.clone())));
}
map_entries.push((Value::Bin(hash.to_vec()), Value::Map(entry)));
}
Some(msgpack::pack(&Value::Map(map_entries)))
}
pub fn build_management_announce(
identity: &rns_crypto::identity::Identity,
rng: &mut dyn rns_crypto::Rng,
) -> Option<Vec<u8>> {
let identity_hash = *identity.hash();
let dest_hash = management_dest_hash(&identity_hash);
let name_hash = rns_core::destination::name_hash("rnstransport", &["remote", "management"]);
let mut random_hash = [0u8; 10];
rng.fill_bytes(&mut random_hash);
let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
identity,
&dest_hash,
&name_hash,
&random_hash,
None, None, )
.ok()?;
let flags = rns_core::packet::PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_ANNOUNCE,
};
let packet = rns_core::packet::RawPacket::pack(
flags,
0,
&dest_hash,
None,
constants::CONTEXT_NONE,
&announce_data,
)
.ok()?;
Some(packet.raw)
}
pub fn build_blackhole_announce(
identity: &rns_crypto::identity::Identity,
rng: &mut dyn rns_crypto::Rng,
) -> Option<Vec<u8>> {
let identity_hash = *identity.hash();
let dest_hash = blackhole_dest_hash(&identity_hash);
let name_hash = rns_core::destination::name_hash("rnstransport", &["info", "blackhole"]);
let mut random_hash = [0u8; 10];
rng.fill_bytes(&mut random_hash);
let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
identity,
&dest_hash,
&name_hash,
&random_hash,
None,
None,
)
.ok()?;
let flags = rns_core::packet::PacketFlags {
header_type: constants::HEADER_1,
context_flag: constants::FLAG_UNSET,
transport_type: constants::TRANSPORT_BROADCAST,
destination_type: constants::DESTINATION_SINGLE,
packet_type: constants::PACKET_TYPE_ANNOUNCE,
};
let packet = rns_core::packet::RawPacket::pack(
flags,
0,
&dest_hash,
None,
constants::CONTEXT_NONE,
&announce_data,
)
.ok()?;
Some(packet.raw)
}