use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::time::{Duration, Instant};
use tox_crypto::*;
use crate::dht::kbucket::*;
use tox_packet::dht::packed_node::*;
use crate::time::*;
pub const PING_INTERVAL: Duration = Duration::from_secs(60);
pub const BAD_NODE_TIMEOUT: Duration = Duration::from_secs(PING_INTERVAL.as_secs() * 2 + 2);
pub const KILL_NODE_TIMEOUT: Duration =
Duration::from_secs(BAD_NODE_TIMEOUT.as_secs() + PING_INTERVAL.as_secs());
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SockAndTime<T: Into<SocketAddr> + Copy> {
pub saddr: Option<T>,
pub last_resp_time: Option<Instant>,
pub last_ping_req_time: Option<Instant>,
pub ret_saddr: Option<T>,
pub ret_last_resp_time: Option<Instant>,
}
impl<T: Into<SocketAddr> + Copy> SockAndTime<T> {
pub fn new(saddr: Option<T>) -> Self {
let last_resp_time = if saddr.is_some() {
Some(clock_now())
} else {
None
};
SockAndTime {
saddr,
last_resp_time,
last_ping_req_time: None,
ret_saddr: None,
ret_last_resp_time: None,
}
}
pub fn is_bad(&self) -> bool {
self.last_resp_time.map_or(true, |time| clock_elapsed(time) > BAD_NODE_TIMEOUT)
}
pub fn is_discarded(&self) -> bool {
self.last_resp_time.map_or(true, |time| clock_elapsed(time) > KILL_NODE_TIMEOUT)
}
pub fn is_ping_interval_passed(&self) -> bool {
self.last_ping_req_time.map_or(true, |time| clock_elapsed(time) >= PING_INTERVAL)
}
pub fn ping_addr(&mut self) -> Option<T> {
if let Some(saddr) = self.saddr {
if !self.is_discarded() && self.is_ping_interval_passed() {
self.last_ping_req_time = Some(clock_now());
Some(saddr)
} else {
None
}
} else {
None
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DhtNode {
pub assoc4: SockAndTime<SocketAddrV4>,
pub assoc6: SockAndTime<SocketAddrV6>,
pub pk: PublicKey,
}
impl DhtNode {
pub fn new(pn: PackedNode) -> DhtNode {
let (saddr_v4, saddr_v6) = match pn.saddr {
SocketAddr::V4(v4) => (Some(v4), None),
SocketAddr::V6(v6) => (None, Some(v6)),
};
DhtNode {
pk: pn.pk,
assoc4: SockAndTime::new(saddr_v4),
assoc6: SockAndTime::new(saddr_v6),
}
}
pub fn is_bad(&self) -> bool {
self.assoc4.is_bad() && self.assoc6.is_bad()
}
pub fn is_discarded(&self) -> bool {
self.assoc4.is_discarded() && self.assoc6.is_discarded()
}
pub fn get_socket_addr(&self) -> Option<SocketAddr> {
let addr = if self.assoc4.last_resp_time >= self.assoc6.last_resp_time {
self.assoc4.saddr.map(Into::into)
} else {
self.assoc6.saddr.map(Into::into)
};
if addr.is_none() {
warn!("get_socket_addr: failed to get address of DhtNode!");
}
addr
}
pub fn get_all_addrs(&self) -> Vec<SocketAddr> {
let addrs = self.assoc4.saddr.into_iter().map(SocketAddr::V4)
.chain(self.assoc6.saddr.into_iter().map(SocketAddr::V6))
.collect::<Vec<_>>();
if addrs.is_empty() {
warn!("get_all_addrs: DhtNode doesn't have IP addresses!");
}
addrs
}
pub fn to_packed_node(&self) -> Option<PackedNode> {
self.get_socket_addr()
.map(|addr| PackedNode::new(addr, &self.pk))
}
pub fn to_all_packed_nodes(&self) -> Vec<PackedNode> {
self.get_all_addrs()
.into_iter()
.map(|addr| PackedNode::new(addr, &self.pk))
.collect()
}
pub fn update_returned_addr(&mut self, addr: SocketAddr) {
match addr {
SocketAddr::V4(v4) => {
self.assoc4.ret_saddr = Some(v4);
self.assoc4.ret_last_resp_time = Some(clock_now());
},
SocketAddr::V6(v6) => {
self.assoc6.ret_saddr = Some(v6);
self.assoc6.ret_last_resp_time = Some(clock_now());
},
}
}
}
impl From<PackedNode> for DhtNode {
fn from(node: PackedNode) -> Self {
DhtNode::new(node)
}
}
impl HasPK for DhtNode {
fn pk(&self) -> PublicKey {
self.pk
}
}
impl KbucketNode for DhtNode {
type NewNode = PackedNode;
type CheckNode = PackedNode;
fn is_outdated(&self, other: &PackedNode) -> bool {
self.assoc4.saddr.map(SocketAddr::V4) != Some(other.saddr) &&
self.assoc6.saddr.map(SocketAddr::V6) != Some(other.saddr)
}
fn update(&mut self, other: &PackedNode) {
match other.saddr {
SocketAddr::V4(sock_v4) => {
self.assoc4.saddr = Some(sock_v4);
self.assoc4.last_resp_time = Some(clock_now());
},
SocketAddr::V6(sock_v6) => {
self.assoc6.saddr = Some(sock_v6);
self.assoc6.last_resp_time = Some(clock_now());
}
}
}
fn is_evictable(&self) -> bool {
self.is_bad()
}
fn eviction_index(nodes: &[Self]) -> Option<usize> {
nodes.iter().rposition(|n| n.is_discarded()).or_else(||
nodes.iter().rposition(|n| n.is_bad())
)
}
}