use std::{collections::HashMap, time::Instant};
use crate::{
error::RnsError,
hash::{AddressHash, Hash},
packet::{DestinationType, Header, HeaderType, IfacFlag, Packet, PacketType},
};
use rmp::encode::write_array_len;
pub struct PathEntry {
pub timestamp: Instant,
pub received_from: AddressHash,
pub hops: u8,
pub iface: AddressHash,
pub packet_hash: Hash,
}
pub struct PathTable {
map: HashMap<AddressHash, PathEntry>,
}
impl PathTable {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn to_msgpack(&self) -> Result<Vec<u8>, RnsError> {
if !self.map.is_empty() {
return Err(RnsError::InvalidArgument);
}
let mut out = Vec::new();
write_array_len(&mut out, 0).map_err(|_| RnsError::InvalidArgument)?;
Ok(out)
}
pub fn get(&self, destination: &AddressHash) -> Option<&PathEntry> {
self.map.get(destination)
}
pub fn next_hop_full(&self, destination: &AddressHash) -> Option<(AddressHash, AddressHash)> {
self.map
.get(destination)
.map(|entry| (entry.received_from, entry.iface))
}
pub fn next_hop_iface(&self, destination: &AddressHash) -> Option<AddressHash> {
self.map.get(destination).map(|entry| entry.iface)
}
pub fn next_hop(&self, destination: &AddressHash) -> Option<AddressHash> {
self.map.get(destination).map(|entry| entry.received_from)
}
pub fn handle_announce(
&mut self,
announce: &Packet,
transport_id: Option<AddressHash>,
iface: AddressHash,
) {
let hops = announce.header.hops + 1;
if let Some(existing_entry) = self.map.get(&announce.destination) {
if hops >= existing_entry.hops {
return;
}
}
let received_from = transport_id.unwrap_or(announce.destination);
let new_entry = PathEntry {
timestamp: Instant::now(),
received_from,
hops,
iface,
packet_hash: announce.hash(),
};
self.map.insert(announce.destination, new_entry);
log::info!(
"{} is now reachable over {} hops through {}",
announce.destination,
hops,
received_from,
);
}
pub fn handle_inbound_packet(
&self,
original_packet: &Packet,
lookup: Option<AddressHash>,
) -> (Packet, Option<AddressHash>) {
let lookup = lookup.unwrap_or(original_packet.destination);
let entry = match self.map.get(&lookup) {
Some(entry) => entry,
None => return (*original_packet, None),
};
(
Packet {
header: Header {
ifac_flag: IfacFlag::Authenticated,
header_type: HeaderType::Type2,
context_flag: original_packet.header.context_flag,
propagation_type: original_packet.header.propagation_type,
destination_type: original_packet.header.destination_type,
packet_type: original_packet.header.packet_type,
hops: original_packet.header.hops + 1,
},
ifac: None,
destination: original_packet.destination,
transport: Some(entry.received_from),
context: original_packet.context,
data: original_packet.data,
},
Some(entry.iface),
)
}
pub fn refresh(&mut self, destination: &AddressHash) {
if let Some(entry) = self.map.get_mut(destination) {
entry.timestamp = Instant::now();
}
}
pub fn handle_packet(&mut self, original_packet: &Packet) -> (Packet, Option<AddressHash>) {
if original_packet.header.header_type == HeaderType::Type2 {
return (*original_packet, None);
}
if original_packet.header.packet_type == PacketType::Announce {
return (*original_packet, None);
}
if original_packet.header.destination_type == DestinationType::Plain
|| original_packet.header.destination_type == DestinationType::Group
{
return (*original_packet, None);
}
let entry = match self.map.get(&original_packet.destination) {
Some(entry) => entry,
None => return (*original_packet, None),
};
(
Packet {
header: Header {
ifac_flag: IfacFlag::Authenticated,
header_type: HeaderType::Type2,
context_flag: original_packet.header.context_flag,
propagation_type: original_packet.header.propagation_type,
destination_type: original_packet.header.destination_type,
packet_type: original_packet.header.packet_type,
hops: original_packet.header.hops,
},
ifac: original_packet.ifac,
destination: original_packet.destination,
transport: Some(entry.received_from),
context: original_packet.context,
data: original_packet.data,
},
Some(entry.iface),
)
}
}
impl Default for PathTable {
fn default() -> Self {
Self::new()
}
}