#![warn(
//unreachable_pub, // TODO
//missing_debug_implementations, // TODO
//missing_docs, // TODO
redundant_lifetimes,
unsafe_code,
non_local_definitions,
clippy::needless_pass_by_value,
clippy::needless_pass_by_ref_mut,
clippy::enum_glob_use
)]
use std::{
array::TryFromSliceError,
net::{AddrParseError, SocketAddr, SocketAddrV4},
sync::Arc,
};
pub use cenc::AnnounceRequestValue;
use cenc::{
NoisePayloadBuilderError, PeerHandshakePayload, PeerHandshakePayloadBuilderError,
RelayThroughInfoBuilderError, UdxInfoBuilderError,
};
use compact_encoding::{CompactEncoding, EncodingError};
use dht_rpc::{IdBytes, InResponse, RpcInnerBuilderError};
use tokio::sync::oneshot::error::RecvError;
use crate::cenc::HandshakeSteps;
mod cenc;
mod crypto;
mod next_router;
mod persistent;
mod server;
pub mod adht;
pub mod announcer;
pub use crypto::{
Keypair, PublicKey, make_signable_announce_or_unannounce, namespace,
sign_announce_or_unannounce,
};
pub use next_router::connection::Connection;
pub use server::Server;
pub const DEFAULT_BOOTSTRAP: [&str; 3] = [
"node1.hyperdht.org:49737",
"node2.hyperdht.org:49737",
"node3.hyperdht.org:49737",
];
pub mod commands {
use dht_rpc::{Command, ExternalCommand};
pub const PEER_HANDSHAKE: Command = Command::External(ExternalCommand(values::PEER_HANDSHAKE));
pub const PEER_HOLEPUNCH: Command = Command::External(ExternalCommand(values::PEER_HOLEPUNCH));
pub const FIND_PEER: Command = Command::External(ExternalCommand(values::FIND_PEER));
pub const LOOKUP: Command = Command::External(ExternalCommand(values::LOOKUP));
pub const ANNOUNCE: Command = Command::External(ExternalCommand(values::ANNOUNCE));
pub const UNANNOUNCE: Command = Command::External(ExternalCommand(values::UNANNOUNCE));
pub mod values {
pub const PEER_HANDSHAKE: usize = 0;
pub const PEER_HOLEPUNCH: usize = 1;
pub const FIND_PEER: usize = 2;
pub const LOOKUP: usize = 3;
pub const ANNOUNCE: usize = 4;
pub const UNANNOUNCE: usize = 5;
}
}
pub const MUTABLE_STORE_CMD: usize = 1;
pub const IMMUTABLE_STORE_CMD: usize = 2;
pub const PEERS_CMD: usize = 3;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Error from dht_rpc: {0}")]
RpcError(#[from] ::dht_rpc::Error),
#[error("Error from compact_encoding: {0}")]
CompactEncodingError(EncodingError),
#[error("IO Eror")]
IoError(#[from] std::io::Error),
#[error("Invalid RPC command in message: {0}")]
InvalidRpcCommand(u8),
#[error("Incorrect message ID size. Expected 32. Error: {0}")]
IncorrectMessageIdSize(TryFromSliceError),
#[error("Error in libsodium's genric_hash function. Return value: {0}")]
LibSodiumGenericHashError(i32),
#[error("RpcDhtBuilderError: {0}")]
RpcDhtBuilderError(#[from] RpcInnerBuilderError),
#[error("RecvError: {0}")]
RecvError(#[from] RecvError),
#[error("AddrParseError: {0}")]
AddrParseError(#[from] AddrParseError),
#[error("Requests must have a 'to' field")]
RequestRequiresToField,
#[error("Ipv6 not supported")]
Ipv6NotSupported,
#[error("Invalid Signature")]
InvalidSignature(i32),
#[error("Error building PeerHandshakePayload: {0}")]
PeerHandshakePayloadBuilder(#[from] PeerHandshakePayloadBuilderError),
#[error("Error building UdxInfo: {0}")]
UdxInfoBuilder(#[from] UdxInfoBuilderError),
#[error("Error building NoisePaylod: {0}")]
NoisePayloadBuilder(#[from] NoisePayloadBuilderError),
#[error("Error building RelayThroughInfo: {0}")]
RelayThroughInfoBuilder(#[from] RelayThroughInfoBuilderError),
#[error("Hypercore Protocol Error: {0}")]
HypercoreProtocolError(#[from] hypercore_handshake::Error),
#[error("Peer handshake failed: {0}")]
PeerHandshakeFailed(String),
#[error("Failed to connect")]
ConnectionFailed,
#[error("Received bad data from a peer: {0}")]
BadMsgFromPeer(String),
#[error("Failed to decode message from peer data from a peer: {0}")]
EncodingErrorFromPeer(EncodingError),
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<EncodingError> for Error {
fn from(value: EncodingError) -> Self {
Error::CompactEncodingError(value)
}
}
#[derive(Debug)]
pub struct PeerHandshakeResponse {
noise: Vec<u8>,
relayed: bool,
server_address: SocketAddrV4,
#[expect(unused)]
client_address: SocketAddrV4,
}
impl PeerHandshakeResponse {
fn new(
noise: Vec<u8>,
relayed: bool,
server_address: SocketAddrV4,
client_address: SocketAddrV4,
) -> Self {
Self {
noise,
relayed,
server_address,
client_address,
}
}
}
pub fn request_announce_or_unannounce_value(
keypair: &Keypair,
target: IdBytes,
token: &[u8; 32],
from: IdBytes,
relay_addresses: &[SocketAddr],
namespace: &[u8; 32],
) -> Vec<u8> {
let announce =
sign_announce_or_unannounce(keypair, target, token, &from.0, relay_addresses, namespace);
announce
.to_encoded_bytes()
.expect("known to succeed for all `Announce` values")
.to_vec()
}
fn decode_peer_handshake_response(resp: &Arc<InResponse>) -> Result<Arc<PeerHandshakeResponse>> {
let hs: PeerHandshakePayload = resp
.response
.value
.as_ref()
.ok_or_else(|| Error::PeerHandshakeFailed("missing value".into()))
.and_then(|value| {
let (hs, _rest) = PeerHandshakePayload::decode(value)?;
debug_assert!(_rest.is_empty());
Ok(hs)
})?;
if !matches!(hs.mode, HandshakeSteps::Reply) || resp.request.to != resp.peer {
return Err(Error::PeerHandshakeFailed("BAD_HANDSHAKE_REPLY".into()));
}
let server_address = if let Some(x) = hs.peer_address {
x
} else {
*resp.request.to.socketv4()?
};
Ok(Arc::new(PeerHandshakeResponse::new(
hs.noise,
hs.peer_address.is_some(),
server_address,
*resp.response.to.socketv4()?,
)))
}