2021-07-12 19:42:48 +01:00
|
|
|
use std::{
|
2023-11-28 18:28:59 +00:00
|
|
|
cmp::Reverse,
|
2021-07-12 19:42:48 +01:00
|
|
|
net::SocketAddr,
|
2023-11-28 07:40:27 +00:00
|
|
|
sync::{
|
|
|
|
|
atomic::{AtomicU16, Ordering},
|
|
|
|
|
Arc,
|
|
|
|
|
},
|
2021-07-14 15:29:59 +01:00
|
|
|
task::Poll,
|
2023-11-28 16:14:49 +00:00
|
|
|
time::{Duration, Instant},
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
bprotocol::{
|
2023-11-28 10:53:22 +00:00
|
|
|
self, CompactNodeInfo, CompactPeerInfo, ErrorDescription, FindNodeRequest, GetPeersRequest,
|
|
|
|
|
Message, MessageKind, Node, PingRequest, Response,
|
2021-07-12 19:42:48 +01:00
|
|
|
},
|
|
|
|
|
routing_table::{InsertResult, RoutingTable},
|
2023-11-28 15:35:27 +00:00
|
|
|
REQUERY_INTERVAL, RESPONSE_TIMEOUT,
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
2023-11-28 15:35:27 +00:00
|
|
|
use anyhow::{bail, Context};
|
2023-11-28 10:53:22 +00:00
|
|
|
use backoff::{backoff::Backoff, ExponentialBackoffBuilder};
|
2023-11-28 11:35:28 +00:00
|
|
|
use bencode::ByteString;
|
2023-11-28 18:28:59 +00:00
|
|
|
use dashmap::DashMap;
|
2023-11-29 16:39:09 +00:00
|
|
|
use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, Stream, StreamExt};
|
2021-07-14 15:29:59 +01:00
|
|
|
use indexmap::IndexSet;
|
2023-11-22 21:56:00 +00:00
|
|
|
use leaky_bucket::RateLimiter;
|
2023-11-25 15:15:16 +00:00
|
|
|
use librqbit_core::{id20::Id20, peer_id::generate_peer_id, spawn_utils::spawn};
|
2022-12-07 22:01:29 +00:00
|
|
|
use parking_lot::RwLock;
|
2021-07-13 18:27:32 +01:00
|
|
|
use rand::Rng;
|
2021-07-14 00:48:53 +01:00
|
|
|
use serde::Serialize;
|
2021-07-12 19:42:48 +01:00
|
|
|
use tokio::{
|
|
|
|
|
net::UdpSocket,
|
2023-11-29 16:39:09 +00:00
|
|
|
sync::{
|
|
|
|
|
mpsc::{channel, unbounded_channel, Sender, UnboundedReceiver, UnboundedSender},
|
|
|
|
|
Notify,
|
|
|
|
|
},
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
2021-07-18 10:53:33 +01:00
|
|
|
use tokio_stream::wrappers::{errors::BroadcastStreamRecvError, BroadcastStream};
|
2023-11-29 16:39:09 +00:00
|
|
|
use tracing::{debug, debug_span, error, error_span, info, trace, warn, Instrument};
|
2021-07-12 19:42:48 +01:00
|
|
|
|
2021-07-14 00:48:53 +01:00
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct DhtStats {
|
2021-07-14 13:40:56 +01:00
|
|
|
#[serde(serialize_with = "crate::utils::serialize_id20")]
|
2021-07-14 00:48:53 +01:00
|
|
|
pub id: Id20,
|
|
|
|
|
pub outstanding_requests: usize,
|
|
|
|
|
pub routing_table_size: usize,
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 08:56:27 +00:00
|
|
|
struct OutstandingRequest {
|
2023-11-28 11:31:34 +00:00
|
|
|
done: tokio::sync::oneshot::Sender<anyhow::Result<ResponseOrError>>,
|
2023-11-28 08:56:27 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 15:55:13 +00:00
|
|
|
pub struct WorkerSendRequest {
|
|
|
|
|
our_tid: Option<u16>,
|
|
|
|
|
message: Message<ByteString>,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 10:40:29 +00:00
|
|
|
#[derive(Debug)]
|
2023-11-28 18:28:59 +00:00
|
|
|
struct MaybeUsefulNode {
|
|
|
|
|
id: Id20,
|
|
|
|
|
addr: SocketAddr,
|
2023-11-29 16:39:09 +00:00
|
|
|
last_request: Instant,
|
2023-11-28 18:28:59 +00:00
|
|
|
last_response: Option<Instant>,
|
2023-11-29 10:40:29 +00:00
|
|
|
returned_peers: bool,
|
2023-11-28 18:28:59 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 19:20:50 +00:00
|
|
|
fn make_rate_limiter() -> RateLimiter {
|
|
|
|
|
// TODO: move to configuration, i'm lazy.
|
|
|
|
|
let dht_queries_per_second = std::env::var("DHT_QUERIES_PER_SECOND")
|
|
|
|
|
.map(|v| v.parse().expect("couldn't parse DHT_QUERIES_PER_SECOND"))
|
|
|
|
|
.unwrap_or(250usize);
|
|
|
|
|
|
|
|
|
|
let per_100_ms = dht_queries_per_second / 10;
|
|
|
|
|
|
|
|
|
|
RateLimiter::builder()
|
|
|
|
|
.initial(per_100_ms)
|
|
|
|
|
.max(dht_queries_per_second)
|
|
|
|
|
.interval(Duration::from_millis(100))
|
|
|
|
|
.fair(false)
|
|
|
|
|
.refill(per_100_ms)
|
|
|
|
|
.build()
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 16:39:09 +00:00
|
|
|
struct RequestPeers {
|
|
|
|
|
info_hash: Id20,
|
|
|
|
|
dht: Arc<DhtState>,
|
|
|
|
|
useful_nodes: RwLock<Vec<MaybeUsefulNode>>,
|
|
|
|
|
tx: tokio::sync::mpsc::UnboundedSender<SocketAddr>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct RequestPeersStream {
|
|
|
|
|
rx: tokio::sync::mpsc::UnboundedReceiver<SocketAddr>,
|
|
|
|
|
cancel_join_handle: tokio::task::JoinHandle<()>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RequestPeersStream {
|
|
|
|
|
fn new(dht: Arc<DhtState>, info_hash: Id20) -> Self {
|
|
|
|
|
let (tx, rx) = unbounded_channel();
|
|
|
|
|
let rp = Arc::new(RequestPeers {
|
|
|
|
|
info_hash,
|
|
|
|
|
dht,
|
|
|
|
|
useful_nodes: RwLock::new(Vec::new()),
|
|
|
|
|
tx,
|
|
|
|
|
});
|
|
|
|
|
let join_handle = rp.request_peers_forever();
|
|
|
|
|
Self {
|
|
|
|
|
rx,
|
|
|
|
|
cancel_join_handle: join_handle,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for RequestPeersStream {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.cancel_join_handle.abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Stream for RequestPeersStream {
|
|
|
|
|
type Item = SocketAddr;
|
|
|
|
|
|
|
|
|
|
fn poll_next(
|
|
|
|
|
mut self: std::pin::Pin<&mut Self>,
|
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
|
) -> Poll<Option<Self::Item>> {
|
|
|
|
|
self.rx.poll_recv(cx)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RequestPeers {
|
|
|
|
|
fn request_peers_forever(self: Arc<Self>) -> tokio::task::JoinHandle<()> {
|
|
|
|
|
spawn(
|
|
|
|
|
error_span!("request_peers", info_hash = format!("{:?}", self.info_hash)),
|
|
|
|
|
async move {
|
|
|
|
|
let mut iteration = 0;
|
|
|
|
|
loop {
|
|
|
|
|
debug!("iteration {}", iteration);
|
|
|
|
|
let sleep_duration = match self.get_peers_root().await {
|
|
|
|
|
Ok(_) => Duration::from_secs(60),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
debug!("error: {e:?}");
|
|
|
|
|
Duration::from_secs(1)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
tokio::time::sleep(sleep_duration).await;
|
|
|
|
|
iteration += 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
fn request_peers_one<'a>(
|
|
|
|
|
self: &'a Arc<Self>,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
) -> BoxFuture<'a, anyhow::Result<()>> {
|
|
|
|
|
let fut = async move {
|
|
|
|
|
let response = self
|
|
|
|
|
.dht
|
|
|
|
|
.request(Request::GetPeers(self.info_hash), addr)
|
|
|
|
|
.await?;
|
|
|
|
|
let response = match response {
|
|
|
|
|
ResponseOrError::Response(r) => r,
|
|
|
|
|
ResponseOrError::Error(e) => {
|
|
|
|
|
bail!("error response: {:?}", e)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.mark_node_responded(addr, &response);
|
|
|
|
|
|
|
|
|
|
if let Some(peers) = response.values {
|
|
|
|
|
for peer in peers {
|
|
|
|
|
self.tx.send(SocketAddr::V4(peer.addr))?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut futs = FuturesUnordered::new();
|
|
|
|
|
if let Some(nodes) = response.nodes {
|
|
|
|
|
for node in nodes.nodes {
|
|
|
|
|
let addr = SocketAddr::V4(node.addr);
|
|
|
|
|
if self.should_request_node(node.id, addr) {
|
|
|
|
|
futs.push(self.request_peers_one(addr));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while let Some(res) = futs.next().await {
|
|
|
|
|
if let Err(e) = res {
|
|
|
|
|
debug!("error: {e:?}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
};
|
|
|
|
|
fut.boxed()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_peers_root(self: &Arc<Self>) -> anyhow::Result<()> {
|
|
|
|
|
let mut futs = FuturesUnordered::new();
|
|
|
|
|
for (_, addr) in self
|
|
|
|
|
.dht
|
|
|
|
|
.routing_table
|
|
|
|
|
.read()
|
|
|
|
|
.sorted_by_distance_from(self.info_hash)
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|n| (n.id(), n.addr()))
|
|
|
|
|
.take(8)
|
|
|
|
|
{
|
|
|
|
|
futs.push(self.request_peers_one(addr))
|
|
|
|
|
}
|
|
|
|
|
if futs.is_empty() {
|
|
|
|
|
bail!("no nodes in routing table")
|
|
|
|
|
}
|
|
|
|
|
while let Some(res) = futs.next().await {
|
|
|
|
|
if let Err(e) = res {
|
|
|
|
|
debug!("error: {e:?}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mark_node_responded(&self, addr: SocketAddr, response: &Response<ByteString>) {
|
|
|
|
|
let mut closest_nodes = self.useful_nodes.write();
|
|
|
|
|
for node in closest_nodes.iter_mut() {
|
|
|
|
|
if node.addr == addr {
|
|
|
|
|
node.last_response = Some(Instant::now());
|
|
|
|
|
node.returned_peers = response
|
|
|
|
|
.values
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|c| !c.is_empty())
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn should_request_node(&self, node_id: Id20, addr: SocketAddr) -> bool {
|
|
|
|
|
let mut closest_nodes = self.useful_nodes.write();
|
|
|
|
|
|
|
|
|
|
// If recently requested, ignore
|
|
|
|
|
if let Some(existing) = closest_nodes.iter_mut().find(|n| n.id == node_id) {
|
|
|
|
|
if existing.last_request.elapsed() > Duration::from_secs(60) {
|
|
|
|
|
existing.last_request = Instant::now();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closest_nodes.push(MaybeUsefulNode {
|
|
|
|
|
id: node_id,
|
|
|
|
|
addr,
|
|
|
|
|
last_request: Instant::now(),
|
|
|
|
|
last_response: None,
|
|
|
|
|
returned_peers: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const LIMIT: usize = 256;
|
|
|
|
|
closest_nodes.sort_by_key(|n| {
|
|
|
|
|
let has_returned_peers_desc = Reverse(n.returned_peers);
|
|
|
|
|
let has_responded_desc = Reverse(n.last_response.is_some() as u8);
|
|
|
|
|
let distance = n.id.distance(&self.info_hash);
|
|
|
|
|
(has_returned_peers_desc, has_responded_desc, distance)
|
|
|
|
|
});
|
|
|
|
|
if closest_nodes.len() > LIMIT {
|
|
|
|
|
let popped = closest_nodes.pop().unwrap();
|
|
|
|
|
if popped.id == node_id {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
2023-11-29 14:48:22 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 08:03:12 +00:00
|
|
|
pub struct DhtState {
|
2021-07-12 19:42:48 +01:00
|
|
|
id: Id20,
|
2023-11-28 07:40:27 +00:00
|
|
|
next_transaction_id: AtomicU16,
|
2023-11-29 16:39:09 +00:00
|
|
|
bootstrapped: Notify,
|
2023-11-27 19:03:39 +00:00
|
|
|
|
|
|
|
|
// Created requests: (transaction_id, addr) => Requests.
|
|
|
|
|
// If we get a response, it gets removed from here.
|
2023-11-28 15:35:27 +00:00
|
|
|
inflight_by_transaction_id: DashMap<(u16, SocketAddr), OutstandingRequest>,
|
2023-11-27 19:03:39 +00:00
|
|
|
|
2023-11-28 08:03:12 +00:00
|
|
|
routing_table: RwLock<RoutingTable>,
|
2021-07-18 15:53:23 +01:00
|
|
|
listen_addr: SocketAddr,
|
2021-07-13 16:28:53 +01:00
|
|
|
|
2023-11-28 10:53:22 +00:00
|
|
|
// Sending requests to the worker.
|
2023-11-28 19:20:50 +00:00
|
|
|
rate_limiter: RateLimiter,
|
2023-11-28 15:55:13 +00:00
|
|
|
sender: UnboundedSender<WorkerSendRequest>,
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DhtState {
|
2023-11-28 08:03:12 +00:00
|
|
|
fn new_internal(
|
2021-07-18 10:53:33 +01:00
|
|
|
id: Id20,
|
2023-11-28 15:55:13 +00:00
|
|
|
sender: UnboundedSender<WorkerSendRequest>,
|
2021-07-18 10:53:33 +01:00
|
|
|
routing_table: Option<RoutingTable>,
|
2021-07-18 15:53:23 +01:00
|
|
|
listen_addr: SocketAddr,
|
2021-07-18 10:53:33 +01:00
|
|
|
) -> Self {
|
|
|
|
|
let routing_table = routing_table.unwrap_or_else(|| RoutingTable::new(id));
|
2021-07-12 19:42:48 +01:00
|
|
|
Self {
|
|
|
|
|
id,
|
2023-11-29 16:39:09 +00:00
|
|
|
bootstrapped: Default::default(),
|
2023-11-28 07:40:27 +00:00
|
|
|
next_transaction_id: AtomicU16::new(0),
|
2023-11-28 15:35:27 +00:00
|
|
|
inflight_by_transaction_id: Default::default(),
|
2023-11-28 08:03:12 +00:00
|
|
|
routing_table: RwLock::new(routing_table),
|
2021-07-12 19:42:48 +01:00
|
|
|
sender,
|
2021-07-18 15:53:23 +01:00
|
|
|
listen_addr,
|
2023-11-28 19:20:50 +00:00
|
|
|
rate_limiter: make_rate_limiter(),
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 10:53:22 +00:00
|
|
|
fn spawn_request(self: &Arc<Self>, request: Request, addr: SocketAddr) {
|
|
|
|
|
let this = self.clone();
|
|
|
|
|
spawn(
|
2023-11-29 13:48:27 +00:00
|
|
|
error_span!(parent: None, "dht_spawn_request", addr=addr.to_string(), request=format!("{:?}", request)),
|
2023-11-28 10:55:43 +00:00
|
|
|
async move {
|
|
|
|
|
match this.send_request_and_handle_response(request, addr).await {
|
|
|
|
|
Ok(_) => {}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
debug!("error: {:?}", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Ok(())
|
|
|
|
|
},
|
2023-11-28 10:53:22 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn send_request_and_handle_response(
|
|
|
|
|
self: &Arc<Self>,
|
|
|
|
|
request: Request,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
let resp = self.request(request, addr).await?;
|
|
|
|
|
match resp {
|
|
|
|
|
ResponseOrError::Response(r) => self.on_response(addr, request, r),
|
|
|
|
|
ResponseOrError::Error(e) => {
|
2023-11-28 15:35:27 +00:00
|
|
|
bail!("received error: {:?}", e);
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn request(&self, request: Request, addr: SocketAddr) -> anyhow::Result<ResponseOrError> {
|
2023-11-28 19:20:50 +00:00
|
|
|
self.rate_limiter.acquire_one().await;
|
2023-11-28 15:55:13 +00:00
|
|
|
let (tid, message) = self.create_request(request);
|
2023-11-28 10:53:22 +00:00
|
|
|
let key = (tid, addr);
|
2023-11-28 08:56:27 +00:00
|
|
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
2023-11-28 15:35:27 +00:00
|
|
|
self.inflight_by_transaction_id
|
|
|
|
|
.insert(key, OutstandingRequest { done: tx });
|
2023-11-28 15:55:13 +00:00
|
|
|
match self.sender.send(WorkerSendRequest {
|
|
|
|
|
our_tid: Some(tid),
|
|
|
|
|
message,
|
|
|
|
|
addr,
|
|
|
|
|
}) {
|
2023-11-28 08:56:27 +00:00
|
|
|
Ok(_) => {}
|
|
|
|
|
Err(e) => {
|
2023-11-28 15:35:27 +00:00
|
|
|
self.inflight_by_transaction_id.remove(&key);
|
2023-11-28 08:56:27 +00:00
|
|
|
return Err(e.into());
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-11-28 10:53:22 +00:00
|
|
|
match tokio::time::timeout(RESPONSE_TIMEOUT, rx).await {
|
2023-11-28 11:31:34 +00:00
|
|
|
Ok(Ok(r)) => r,
|
2023-11-28 10:53:22 +00:00
|
|
|
Ok(Err(e)) => {
|
2023-11-28 15:35:27 +00:00
|
|
|
self.inflight_by_transaction_id.remove(&key);
|
2023-11-28 10:53:22 +00:00
|
|
|
warn!("recv error, did not expect this: {:?}", e);
|
|
|
|
|
Err(e.into())
|
|
|
|
|
}
|
2023-11-28 11:31:34 +00:00
|
|
|
Err(_) => {
|
2023-11-28 15:35:27 +00:00
|
|
|
self.inflight_by_transaction_id.remove(&key);
|
|
|
|
|
bail!("timeout")
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-28 07:40:27 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 08:03:12 +00:00
|
|
|
fn create_request(&self, request: Request) -> (u16, Message<ByteString>) {
|
2023-11-28 07:40:27 +00:00
|
|
|
let transaction_id = self.next_transaction_id.fetch_add(1, Ordering::Relaxed);
|
2021-07-12 19:42:48 +01:00
|
|
|
let transaction_id_buf = [(transaction_id >> 8) as u8, (transaction_id & 0xff) as u8];
|
2023-04-23 17:55:52 +02:00
|
|
|
|
2021-07-12 19:42:48 +01:00
|
|
|
let message = match request {
|
|
|
|
|
Request::GetPeers(info_hash) => Message {
|
|
|
|
|
transaction_id: ByteString::from(transaction_id_buf.as_ref()),
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
|
|
|
|
kind: MessageKind::GetPeersRequest(GetPeersRequest {
|
|
|
|
|
id: self.id,
|
|
|
|
|
info_hash,
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
Request::FindNode(target) => Message {
|
|
|
|
|
transaction_id: ByteString::from(transaction_id_buf.as_ref()),
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
|
|
|
|
kind: MessageKind::FindNodeRequest(FindNodeRequest {
|
|
|
|
|
id: self.id,
|
|
|
|
|
target,
|
|
|
|
|
}),
|
|
|
|
|
},
|
2023-11-27 18:53:20 +00:00
|
|
|
Request::Ping => Message {
|
|
|
|
|
transaction_id: ByteString::from(transaction_id_buf.as_ref()),
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
|
|
|
|
kind: MessageKind::PingRequest(PingRequest { id: self.id }),
|
|
|
|
|
},
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
2023-11-28 07:40:27 +00:00
|
|
|
(transaction_id, message)
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
2023-11-27 19:03:39 +00:00
|
|
|
|
2023-11-28 09:23:05 +00:00
|
|
|
fn on_response(
|
|
|
|
|
self: &Arc<Self>,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
request: Request,
|
|
|
|
|
response: Response<ByteString>,
|
|
|
|
|
) -> anyhow::Result<()> {
|
2023-11-29 10:40:29 +00:00
|
|
|
self.routing_table.write().mark_response(&response.id);
|
2023-11-28 09:23:05 +00:00
|
|
|
match request {
|
|
|
|
|
Request::FindNode(id) => {
|
|
|
|
|
let nodes = response
|
|
|
|
|
.nodes
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("expected nodes for find_node requests"))?;
|
|
|
|
|
self.on_found_nodes(response.id, addr, id, nodes)
|
|
|
|
|
}
|
|
|
|
|
Request::Ping => Ok(()),
|
2023-11-28 15:35:27 +00:00
|
|
|
Request::GetPeers(info_hash) => {
|
2023-11-29 16:39:09 +00:00
|
|
|
todo!()
|
|
|
|
|
// self.on_found_peers_or_nodes(response.id, addr, info_hash, response)
|
2023-11-28 15:35:27 +00:00
|
|
|
}
|
2023-11-28 09:23:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 15:35:27 +00:00
|
|
|
fn on_received_message(
|
2023-11-28 08:03:12 +00:00
|
|
|
self: &Arc<Self>,
|
2021-07-12 19:42:48 +01:00
|
|
|
msg: Message<ByteString>,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
) -> anyhow::Result<()> {
|
2021-07-13 18:27:32 +01:00
|
|
|
let generate_compact_nodes = |target| {
|
|
|
|
|
let nodes = self
|
|
|
|
|
.routing_table
|
2023-11-28 08:03:12 +00:00
|
|
|
.read()
|
2021-07-13 18:27:32 +01:00
|
|
|
.sorted_by_distance_from(target)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|r| {
|
|
|
|
|
Some(Node {
|
|
|
|
|
id: r.id(),
|
|
|
|
|
addr: match r.addr() {
|
|
|
|
|
SocketAddr::V4(v4) => v4,
|
|
|
|
|
SocketAddr::V6(_) => return None,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.take(8)
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
CompactNodeInfo { nodes }
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-12 19:42:48 +01:00
|
|
|
match &msg.kind {
|
2023-11-28 10:53:22 +00:00
|
|
|
// If it's a response to a request we made, find the request task, notify it with the response,
|
|
|
|
|
// and let it handle it.
|
2021-07-14 00:06:09 +01:00
|
|
|
MessageKind::Error(_) | MessageKind::Response(_) => {
|
2023-11-28 15:35:27 +00:00
|
|
|
let tid = msg.get_our_transaction_id().context("bad transaction id")?;
|
|
|
|
|
let request = match self
|
|
|
|
|
.inflight_by_transaction_id
|
|
|
|
|
.remove(&(tid, addr))
|
|
|
|
|
.map(|(_, v)| v)
|
|
|
|
|
{
|
2021-07-14 00:48:53 +01:00
|
|
|
Some(req) => req,
|
2023-11-28 15:35:27 +00:00
|
|
|
None => bail!("outstanding request not found. Message: {:?}", msg),
|
2021-07-14 00:48:53 +01:00
|
|
|
};
|
2023-11-28 10:53:22 +00:00
|
|
|
|
|
|
|
|
let response_or_error = match msg.kind {
|
|
|
|
|
MessageKind::Error(e) => ResponseOrError::Error(e),
|
2023-11-29 10:40:29 +00:00
|
|
|
MessageKind::Response(r) => ResponseOrError::Response(r),
|
2021-07-14 00:06:09 +01:00
|
|
|
_ => unreachable!(),
|
|
|
|
|
};
|
2023-11-28 11:31:34 +00:00
|
|
|
match request.done.send(Ok(response_or_error)) {
|
2023-11-28 10:53:22 +00:00
|
|
|
Ok(_) => {}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warn!(
|
|
|
|
|
"recieved response, but the receiver task is closed: {:?}",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
2021-07-14 00:06:09 +01:00
|
|
|
}
|
2023-11-28 10:53:22 +00:00
|
|
|
// Otherwise, respond to a query.
|
|
|
|
|
MessageKind::PingRequest(req) => {
|
2021-07-12 19:42:48 +01:00
|
|
|
let message = Message {
|
|
|
|
|
transaction_id: msg.transaction_id,
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
2021-07-14 00:48:53 +01:00
|
|
|
kind: MessageKind::Response(bprotocol::Response {
|
|
|
|
|
id: self.id,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
2023-11-28 10:53:22 +00:00
|
|
|
self.routing_table.write().mark_last_query(&req.id);
|
2023-11-28 15:55:13 +00:00
|
|
|
self.sender.send(WorkerSendRequest {
|
|
|
|
|
our_tid: None,
|
|
|
|
|
message,
|
|
|
|
|
addr,
|
|
|
|
|
})?;
|
2021-07-14 00:06:09 +01:00
|
|
|
Ok(())
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
2021-07-13 18:27:32 +01:00
|
|
|
MessageKind::GetPeersRequest(req) => {
|
2023-11-29 16:39:09 +00:00
|
|
|
// let peers = self.info_hash_meta.get(&req.info_hash).map(|meta| {
|
|
|
|
|
// meta.seen_peers
|
|
|
|
|
// .iter()
|
|
|
|
|
// .copied()
|
|
|
|
|
// .filter_map(|a| match a {
|
|
|
|
|
// SocketAddr::V4(v4) => Some(CompactPeerInfo { addr: v4 }),
|
|
|
|
|
// // this should never happen in practice
|
|
|
|
|
// SocketAddr::V6(_) => None,
|
|
|
|
|
// })
|
|
|
|
|
// .take(50)
|
|
|
|
|
// .collect::<Vec<_>>()
|
|
|
|
|
// });
|
|
|
|
|
// let token = if peers.is_some() {
|
|
|
|
|
// let mut token = [0u8; 20];
|
|
|
|
|
// rand::thread_rng().fill(&mut token);
|
|
|
|
|
// Some(ByteString::from(token.as_ref()))
|
|
|
|
|
// } else {
|
|
|
|
|
// None
|
|
|
|
|
// };
|
|
|
|
|
// let compact_node_info = generate_compact_nodes(req.info_hash);
|
2023-11-28 08:03:12 +00:00
|
|
|
self.routing_table.write().mark_last_query(&req.id);
|
2021-07-13 18:27:32 +01:00
|
|
|
let message = Message {
|
|
|
|
|
transaction_id: msg.transaction_id,
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
2021-07-14 00:48:53 +01:00
|
|
|
kind: MessageKind::Response(bprotocol::Response {
|
|
|
|
|
id: self.id,
|
2023-11-29 16:39:09 +00:00
|
|
|
nodes: None,
|
|
|
|
|
values: None,
|
|
|
|
|
token: None,
|
2021-07-14 00:48:53 +01:00
|
|
|
}),
|
2021-07-13 18:27:32 +01:00
|
|
|
};
|
2023-11-28 15:55:13 +00:00
|
|
|
self.sender.send(WorkerSendRequest {
|
|
|
|
|
our_tid: None,
|
|
|
|
|
message,
|
|
|
|
|
addr,
|
|
|
|
|
})?;
|
2021-07-14 00:06:09 +01:00
|
|
|
Ok(())
|
2021-07-13 18:27:32 +01:00
|
|
|
}
|
|
|
|
|
MessageKind::FindNodeRequest(req) => {
|
|
|
|
|
let compact_node_info = generate_compact_nodes(req.target);
|
2023-11-28 08:03:12 +00:00
|
|
|
self.routing_table.write().mark_last_query(&req.id);
|
2021-07-12 19:42:48 +01:00
|
|
|
let message = Message {
|
|
|
|
|
transaction_id: msg.transaction_id,
|
|
|
|
|
version: None,
|
|
|
|
|
ip: None,
|
2021-07-14 00:48:53 +01:00
|
|
|
kind: MessageKind::Response(bprotocol::Response {
|
|
|
|
|
id: self.id,
|
|
|
|
|
nodes: Some(compact_node_info),
|
|
|
|
|
..Default::default()
|
|
|
|
|
}),
|
2021-07-12 19:42:48 +01:00
|
|
|
};
|
2023-11-28 15:55:13 +00:00
|
|
|
self.sender.send(WorkerSendRequest {
|
|
|
|
|
our_tid: None,
|
|
|
|
|
message,
|
|
|
|
|
addr,
|
|
|
|
|
})?;
|
2021-07-14 00:06:09 +01:00
|
|
|
Ok(())
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 00:48:53 +01:00
|
|
|
pub fn get_stats(&self) -> DhtStats {
|
|
|
|
|
DhtStats {
|
|
|
|
|
id: self.id,
|
2023-11-28 15:35:27 +00:00
|
|
|
outstanding_requests: self.inflight_by_transaction_id.len(),
|
2023-11-28 08:03:12 +00:00
|
|
|
routing_table_size: self.routing_table.read().len(),
|
2021-07-14 00:48:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 15:35:27 +00:00
|
|
|
fn send_request_if_not_yet(
|
|
|
|
|
self: &Arc<Self>,
|
|
|
|
|
target_node: Id20,
|
|
|
|
|
request: Request,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
let this = self.clone();
|
|
|
|
|
let fut = async move {
|
2023-11-28 16:14:49 +00:00
|
|
|
this.routing_table
|
|
|
|
|
.write()
|
|
|
|
|
.mark_outgoing_request(&target_node);
|
|
|
|
|
|
|
|
|
|
let resp = this.request(request, addr).await;
|
|
|
|
|
match resp {
|
|
|
|
|
Ok(ResponseOrError::Response(response)) => {
|
2023-11-29 10:40:29 +00:00
|
|
|
this.routing_table.write().mark_response(&target_node);
|
2023-11-28 16:14:49 +00:00
|
|
|
match this.on_response(addr, request, response) {
|
|
|
|
|
Ok(()) => {}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warn!("error in on_response: {:?}", e);
|
2023-11-28 15:35:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-28 16:14:49 +00:00
|
|
|
Ok(ResponseOrError::Error(e)) => {
|
2023-11-29 10:40:29 +00:00
|
|
|
this.routing_table.write().mark_response(&target_node);
|
2023-11-28 16:14:49 +00:00
|
|
|
debug!("error response: {:?}", e);
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
2023-11-29 10:40:29 +00:00
|
|
|
this.routing_table.write().mark_error(&target_node);
|
2023-11-28 16:14:49 +00:00
|
|
|
debug!("error: {:?}", e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Ok(())
|
2023-11-28 15:35:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
spawn(
|
|
|
|
|
error_span!(
|
|
|
|
|
parent: None,
|
|
|
|
|
"dht_request",
|
|
|
|
|
addr = addr.to_string(),
|
|
|
|
|
request = format!("{:?}", request),
|
|
|
|
|
),
|
|
|
|
|
fut,
|
|
|
|
|
);
|
2021-07-14 00:48:53 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send_find_node_if_not_yet(
|
2023-11-28 08:03:12 +00:00
|
|
|
self: &Arc<Self>,
|
2021-07-14 00:48:53 +01:00
|
|
|
search_id: Id20,
|
|
|
|
|
target_node: Id20,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
|
) -> anyhow::Result<()> {
|
2023-11-28 15:35:27 +00:00
|
|
|
self.send_request_if_not_yet(target_node, Request::FindNode(search_id), addr)
|
2021-07-14 00:48:53 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 08:03:12 +00:00
|
|
|
fn routing_table_add_node(self: &Arc<Self>, id: Id20, addr: SocketAddr) -> InsertResult {
|
2023-11-27 18:53:20 +00:00
|
|
|
let mut questionable_nodes = Vec::new();
|
2023-11-28 08:03:12 +00:00
|
|
|
let res = self.routing_table.write().add_node(id, addr, |addr| {
|
2023-11-27 18:53:20 +00:00
|
|
|
questionable_nodes.push(addr);
|
|
|
|
|
true
|
|
|
|
|
});
|
|
|
|
|
for addr in questionable_nodes {
|
2023-11-28 10:53:22 +00:00
|
|
|
self.spawn_request(Request::Ping, addr);
|
2023-11-27 18:53:20 +00:00
|
|
|
}
|
|
|
|
|
res
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-12 19:42:48 +01:00
|
|
|
fn on_found_nodes(
|
2023-11-28 08:03:12 +00:00
|
|
|
self: &Arc<Self>,
|
2021-07-12 19:42:48 +01:00
|
|
|
source: Id20,
|
|
|
|
|
source_addr: SocketAddr,
|
2021-07-14 00:48:53 +01:00
|
|
|
target: Id20,
|
2021-07-12 19:42:48 +01:00
|
|
|
nodes: CompactNodeInfo,
|
|
|
|
|
) -> anyhow::Result<()> {
|
2023-11-29 16:39:09 +00:00
|
|
|
self.routing_table_add_node(source, source_addr);
|
2021-07-12 19:42:48 +01:00
|
|
|
for node in nodes.nodes {
|
2023-11-27 18:53:20 +00:00
|
|
|
match self.routing_table_add_node(node.id, node.addr.into()) {
|
2021-07-12 19:42:48 +01:00
|
|
|
InsertResult::ReplacedBad(_) | InsertResult::Added => {
|
2021-07-14 00:48:53 +01:00
|
|
|
// recursively find nodes closest to us until we can't find more.
|
|
|
|
|
self.send_find_node_if_not_yet(target, source, source_addr)?;
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
|
enum Request {
|
|
|
|
|
GetPeers(Id20),
|
|
|
|
|
FindNode(Id20),
|
2023-11-27 18:53:20 +00:00
|
|
|
Ping,
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 10:53:22 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
enum ResponseOrError {
|
|
|
|
|
Response(Response<ByteString>),
|
|
|
|
|
Error(ErrorDescription<ByteString>),
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-12 19:42:48 +01:00
|
|
|
struct DhtWorker {
|
|
|
|
|
socket: UdpSocket,
|
|
|
|
|
peer_id: Id20,
|
2023-11-28 08:03:12 +00:00
|
|
|
state: Arc<DhtState>,
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DhtWorker {
|
2023-11-28 11:31:34 +00:00
|
|
|
fn on_send_error(&self, tid: u16, addr: SocketAddr, err: anyhow::Error) {
|
2023-11-28 15:35:27 +00:00
|
|
|
if let Some((_, OutstandingRequest { done })) =
|
|
|
|
|
self.state.inflight_by_transaction_id.remove(&(tid, addr))
|
|
|
|
|
{
|
2023-11-28 11:31:34 +00:00
|
|
|
let _ = done.send(Err(err)).is_err();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 10:53:22 +00:00
|
|
|
async fn bootstrap_one_ip_with_backoff(&self, addr: SocketAddr) -> anyhow::Result<()> {
|
|
|
|
|
let mut backoff = ExponentialBackoffBuilder::new()
|
|
|
|
|
.with_initial_interval(Duration::from_secs(10))
|
|
|
|
|
.with_multiplier(1.5)
|
|
|
|
|
.with_max_interval(Duration::from_secs(60))
|
|
|
|
|
.with_max_elapsed_time(Some(Duration::from_secs(86400)))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let res = self
|
|
|
|
|
.state
|
|
|
|
|
.send_request_and_handle_response(Request::FindNode(self.peer_id), addr)
|
|
|
|
|
.await;
|
|
|
|
|
match res {
|
|
|
|
|
Ok(r) => return Ok(r),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
debug!("error: {:?}", e);
|
|
|
|
|
if let Some(backoff) = backoff.next_backoff() {
|
|
|
|
|
tokio::time::sleep(backoff).await;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-11-28 15:35:27 +00:00
|
|
|
bail!("given up bootstrapping, timed out")
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn bootstrap_hostname(&self, hostname: &str) -> anyhow::Result<()> {
|
|
|
|
|
let addrs = tokio::net::lookup_host(hostname)
|
|
|
|
|
.await
|
|
|
|
|
.with_context(|| format!("error looking up {}", hostname))?;
|
|
|
|
|
let mut futs = FuturesUnordered::new();
|
|
|
|
|
for addr in addrs {
|
|
|
|
|
futs.push(
|
|
|
|
|
self.bootstrap_one_ip_with_backoff(addr)
|
|
|
|
|
.instrument(error_span!("addr", addr = addr.to_string())),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
let requests = futs.len();
|
|
|
|
|
let mut successes = 0;
|
|
|
|
|
while let Some(resp) = futs.next().await {
|
|
|
|
|
if resp.is_ok() {
|
|
|
|
|
successes += 1
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if successes == 0 {
|
2023-11-28 15:35:27 +00:00
|
|
|
bail!("none of the {} bootstrap requests succeded", requests);
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn bootstrap_hostname_with_backoff(&self, addr: &str) -> anyhow::Result<()> {
|
|
|
|
|
let mut backoff = ExponentialBackoffBuilder::new()
|
|
|
|
|
.with_initial_interval(Duration::from_secs(10))
|
|
|
|
|
.with_multiplier(1.5)
|
|
|
|
|
.with_max_interval(Duration::from_secs(60))
|
|
|
|
|
.with_max_elapsed_time(Some(Duration::from_secs(86400)))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let backoff = match self.bootstrap_hostname(addr).await {
|
|
|
|
|
Ok(_) => return Ok(()),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
warn!("error: {}", e);
|
|
|
|
|
backoff.next_backoff()
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if let Some(backoff) = backoff {
|
|
|
|
|
tokio::time::sleep(backoff).await;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-11-28 15:35:27 +00:00
|
|
|
bail!("bootstrap failed")
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn bootstrap(&self, bootstrap_addrs: &[String]) -> anyhow::Result<()> {
|
|
|
|
|
let mut futs = FuturesUnordered::new();
|
|
|
|
|
|
|
|
|
|
for addr in bootstrap_addrs.iter() {
|
|
|
|
|
let this = &self;
|
|
|
|
|
futs.push(
|
|
|
|
|
this.bootstrap_hostname_with_backoff(addr)
|
|
|
|
|
.instrument(error_span!("bootstrap", hostname = addr)),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
let mut successes = 0;
|
|
|
|
|
while let Some(resp) = futs.next().await {
|
|
|
|
|
if resp.is_ok() {
|
|
|
|
|
successes += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if successes == 0 {
|
2023-11-28 15:35:27 +00:00
|
|
|
bail!("bootstrapping failed")
|
2023-11-28 10:53:22 +00:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 11:31:34 +00:00
|
|
|
async fn framer(
|
|
|
|
|
&self,
|
|
|
|
|
socket: &UdpSocket,
|
2023-11-28 15:55:13 +00:00
|
|
|
mut input_rx: UnboundedReceiver<WorkerSendRequest>,
|
2023-11-28 11:31:34 +00:00
|
|
|
output_tx: Sender<(Message<ByteString>, SocketAddr)>,
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
let writer = async {
|
|
|
|
|
let mut buf = Vec::new();
|
2023-11-28 15:55:13 +00:00
|
|
|
while let Some(WorkerSendRequest {
|
|
|
|
|
our_tid,
|
|
|
|
|
message,
|
|
|
|
|
addr,
|
|
|
|
|
}) = input_rx.recv().await
|
|
|
|
|
{
|
|
|
|
|
trace!("{}: sending {:?}", addr, &message);
|
2023-11-28 11:31:34 +00:00
|
|
|
buf.clear();
|
|
|
|
|
bprotocol::serialize_message(
|
|
|
|
|
&mut buf,
|
2023-11-28 15:55:13 +00:00
|
|
|
message.transaction_id,
|
|
|
|
|
message.version,
|
|
|
|
|
message.ip,
|
|
|
|
|
message.kind,
|
2023-11-28 11:31:34 +00:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
if let Err(e) = socket.send_to(&buf, addr).await {
|
2023-11-28 15:35:27 +00:00
|
|
|
debug!("error sending to {addr}: {e:?}");
|
2023-11-28 15:55:13 +00:00
|
|
|
if let Some(tid) = our_tid {
|
2023-11-28 15:35:27 +00:00
|
|
|
self.on_send_error(tid, addr, e.into());
|
|
|
|
|
}
|
2023-11-28 11:31:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err::<(), _>(anyhow::anyhow!(
|
|
|
|
|
"DHT UDP socket writer over, nowhere to read messages from"
|
|
|
|
|
))
|
|
|
|
|
};
|
|
|
|
|
let reader = async {
|
|
|
|
|
let mut buf = vec![0u8; 16384];
|
|
|
|
|
loop {
|
|
|
|
|
let (size, addr) = socket
|
|
|
|
|
.recv_from(&mut buf)
|
|
|
|
|
.await
|
|
|
|
|
.context("error reading from UDP socket")?;
|
|
|
|
|
match bprotocol::deserialize_message::<ByteString>(&buf[..size]) {
|
|
|
|
|
Ok(msg) => {
|
|
|
|
|
trace!("{}: received {:?}", addr, &msg);
|
|
|
|
|
match output_tx.send((msg, addr)).await {
|
|
|
|
|
Ok(_) => {}
|
|
|
|
|
Err(_) => break,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => debug!("{}: error deserializing incoming message: {}", addr, e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err::<(), _>(anyhow::anyhow!(
|
|
|
|
|
"DHT UDP socket reader over, nowhere to send responses to"
|
|
|
|
|
))
|
|
|
|
|
};
|
|
|
|
|
let result = tokio::select! {
|
|
|
|
|
err = writer => err,
|
|
|
|
|
err = reader => err,
|
|
|
|
|
};
|
|
|
|
|
result.context("DHT UDP framer closed")
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-12 19:42:48 +01:00
|
|
|
async fn start(
|
|
|
|
|
self,
|
2023-11-28 15:55:13 +00:00
|
|
|
in_rx: UnboundedReceiver<WorkerSendRequest>,
|
2021-07-12 19:42:48 +01:00
|
|
|
bootstrap_addrs: &[String],
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
let (out_tx, mut out_rx) = channel(1);
|
2023-11-28 11:31:34 +00:00
|
|
|
let framer = self
|
|
|
|
|
.framer(&self.socket, in_rx, out_tx)
|
|
|
|
|
.instrument(debug_span!("dht_framer"));
|
2021-07-12 19:42:48 +01:00
|
|
|
|
2023-11-28 10:53:22 +00:00
|
|
|
let bootstrap = self.bootstrap(bootstrap_addrs);
|
2021-07-12 19:42:48 +01:00
|
|
|
let mut bootstrap_done = false;
|
|
|
|
|
|
|
|
|
|
let response_reader = {
|
|
|
|
|
let this = &self;
|
|
|
|
|
async move {
|
|
|
|
|
while let Some((response, addr)) = out_rx.recv().await {
|
2023-11-28 15:35:27 +00:00
|
|
|
if let Err(e) = this.state.on_received_message(response, addr) {
|
2021-07-12 19:42:48 +01:00
|
|
|
debug!("error in on_response, addr={:?}: {}", addr, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err::<(), _>(anyhow::anyhow!(
|
|
|
|
|
"closed response reader, nowhere to send results to, DHT closed"
|
|
|
|
|
))
|
|
|
|
|
}
|
2023-11-25 15:15:16 +00:00
|
|
|
}
|
|
|
|
|
.instrument(debug_span!("dht_responese_reader"));
|
2021-07-12 19:42:48 +01:00
|
|
|
|
|
|
|
|
tokio::pin!(framer);
|
|
|
|
|
tokio::pin!(bootstrap);
|
|
|
|
|
tokio::pin!(response_reader);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
tokio::select! {
|
|
|
|
|
err = &mut framer => {
|
|
|
|
|
anyhow::bail!("framer quit: {:?}", err)
|
|
|
|
|
},
|
|
|
|
|
result = &mut bootstrap, if !bootstrap_done => {
|
|
|
|
|
bootstrap_done = true;
|
|
|
|
|
result?;
|
|
|
|
|
},
|
|
|
|
|
err = &mut response_reader => {anyhow::bail!("response reader quit: {:?}", err)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 15:29:59 +01:00
|
|
|
struct PeerStream {
|
|
|
|
|
info_hash: Id20,
|
2023-11-28 08:03:12 +00:00
|
|
|
state: Arc<DhtState>,
|
2021-07-14 15:29:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Stream for PeerStream {
|
|
|
|
|
type Item = SocketAddr;
|
|
|
|
|
|
|
|
|
|
fn poll_next(
|
|
|
|
|
mut self: std::pin::Pin<&mut Self>,
|
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
|
) -> Poll<Option<Self::Item>> {
|
2023-11-29 16:39:09 +00:00
|
|
|
todo!()
|
2021-07-14 15:29:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-18 10:53:33 +01:00
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct DhtConfig {
|
|
|
|
|
pub peer_id: Option<Id20>,
|
|
|
|
|
pub bootstrap_addrs: Option<Vec<String>>,
|
|
|
|
|
pub routing_table: Option<RoutingTable>,
|
2021-07-18 15:53:23 +01:00
|
|
|
pub listen_addr: Option<SocketAddr>,
|
2021-07-18 10:53:33 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-28 08:03:12 +00:00
|
|
|
impl DhtState {
|
|
|
|
|
pub async fn new() -> anyhow::Result<Arc<Self>> {
|
2021-07-18 10:53:33 +01:00
|
|
|
Self::with_config(DhtConfig::default()).await
|
2021-07-12 21:59:08 +01:00
|
|
|
}
|
2023-11-28 08:03:12 +00:00
|
|
|
pub async fn with_config(config: DhtConfig) -> anyhow::Result<Arc<Self>> {
|
2021-07-18 15:53:23 +01:00
|
|
|
let socket = match config.listen_addr {
|
2021-09-29 19:15:04 +01:00
|
|
|
Some(addr) => UdpSocket::bind(addr)
|
|
|
|
|
.await
|
2022-12-07 22:01:29 +00:00
|
|
|
.with_context(|| format!("error binding socket, address {addr}")),
|
2021-09-29 19:15:04 +01:00
|
|
|
None => UdpSocket::bind("0.0.0.0:0")
|
|
|
|
|
.await
|
|
|
|
|
.context("error binding socket, address 0.0.0.0:0"),
|
|
|
|
|
}?;
|
2021-07-18 15:53:23 +01:00
|
|
|
|
|
|
|
|
let listen_addr = socket
|
|
|
|
|
.local_addr()
|
|
|
|
|
.context("cannot determine UDP listen addr")?;
|
|
|
|
|
info!("DHT listening on {:?}", listen_addr);
|
|
|
|
|
|
2021-07-18 10:53:33 +01:00
|
|
|
let peer_id = config.peer_id.unwrap_or_else(generate_peer_id);
|
2021-07-12 19:42:48 +01:00
|
|
|
info!("starting up DHT with peer id {:?}", peer_id);
|
2021-07-18 10:53:33 +01:00
|
|
|
let bootstrap_addrs = config
|
|
|
|
|
.bootstrap_addrs
|
|
|
|
|
.unwrap_or_else(|| crate::DHT_BOOTSTRAP.iter().map(|v| v.to_string()).collect());
|
2021-07-12 19:42:48 +01:00
|
|
|
|
2021-07-13 16:10:36 +01:00
|
|
|
let (in_tx, in_rx) = unbounded_channel();
|
2023-11-28 08:03:12 +00:00
|
|
|
let state = Arc::new(Self::new_internal(
|
2021-07-18 10:53:33 +01:00
|
|
|
peer_id,
|
2023-11-28 07:40:27 +00:00
|
|
|
in_tx,
|
2021-07-18 10:53:33 +01:00
|
|
|
config.routing_table,
|
2021-07-18 15:53:23 +01:00
|
|
|
listen_addr,
|
2023-11-28 08:03:12 +00:00
|
|
|
));
|
2021-07-13 16:10:36 +01:00
|
|
|
|
2023-11-25 15:15:16 +00:00
|
|
|
spawn(error_span!("dht"), {
|
2021-07-13 16:10:36 +01:00
|
|
|
let state = state.clone();
|
|
|
|
|
async move {
|
|
|
|
|
let worker = DhtWorker {
|
|
|
|
|
socket,
|
|
|
|
|
peer_id,
|
|
|
|
|
state,
|
|
|
|
|
};
|
2023-11-28 07:40:27 +00:00
|
|
|
worker.start(in_rx, &bootstrap_addrs).await?;
|
2023-11-25 15:15:16 +00:00
|
|
|
Ok(())
|
2021-07-13 16:10:36 +01:00
|
|
|
}
|
2021-07-12 19:42:48 +01:00
|
|
|
});
|
2023-11-28 08:03:12 +00:00
|
|
|
Ok(state)
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
2023-11-24 14:19:39 +00:00
|
|
|
|
|
|
|
|
pub fn get_peers(
|
2023-11-28 08:03:12 +00:00
|
|
|
self: &Arc<Self>,
|
2021-07-13 16:10:36 +01:00
|
|
|
info_hash: Id20,
|
2021-07-14 15:29:59 +01:00
|
|
|
) -> anyhow::Result<impl Stream<Item = SocketAddr> + Unpin> {
|
2023-11-29 16:39:09 +00:00
|
|
|
Ok(RequestPeersStream::new(self.clone(), info_hash))
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|
2021-07-14 00:48:53 +01:00
|
|
|
|
2021-07-18 15:53:23 +01:00
|
|
|
pub fn listen_addr(&self) -> SocketAddr {
|
2023-11-28 08:03:12 +00:00
|
|
|
self.listen_addr
|
2021-07-18 15:53:23 +01:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 00:48:53 +01:00
|
|
|
pub fn stats(&self) -> DhtStats {
|
2023-11-28 08:03:12 +00:00
|
|
|
self.get_stats()
|
2021-07-14 00:48:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn with_routing_table<R, F: FnOnce(&RoutingTable) -> R>(&self, f: F) -> R {
|
2023-11-28 08:03:12 +00:00
|
|
|
f(&self.routing_table.read())
|
2021-07-14 00:48:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn clone_routing_table(&self) -> RoutingTable {
|
2023-11-28 08:03:12 +00:00
|
|
|
self.routing_table.read().clone()
|
2021-07-14 00:48:53 +01:00
|
|
|
}
|
2021-07-12 19:42:48 +01:00
|
|
|
}
|