use crate::{Result, QsshError};
use super::{PeerInfo, P2pSession};
use tokio::net::UdpSocket;
use std::sync::Arc;
use tokio::sync::RwLock;
use std::time::Duration;
#[derive(Debug, Clone)]
pub enum DiscoveryMethod {
LocalBroadcast,
Dht,
Blockchain,
Manual(Vec<String>),
}
pub struct Discovery {
method: DiscoveryMethod,
session: Arc<P2pSession>,
discovered_peers: Arc<RwLock<Vec<PeerInfo>>>,
}
impl Discovery {
pub fn new(method: DiscoveryMethod, session: Arc<P2pSession>) -> Self {
Self {
method,
session,
discovered_peers: Arc::new(RwLock::new(Vec::new())),
}
}
pub async fn start(&self) -> Result<()> {
match &self.method {
DiscoveryMethod::LocalBroadcast => self.local_broadcast_discovery().await,
DiscoveryMethod::Dht => self.dht_discovery().await,
DiscoveryMethod::Blockchain => self.blockchain_discovery().await,
DiscoveryMethod::Manual(peers) => self.manual_discovery(peers).await,
}
}
async fn local_broadcast_discovery(&self) -> Result<()> {
let bind_addr = std::env::var("QSSH_DISCOVERY_BIND")
.unwrap_or_else(|_| "127.0.0.1:22223".to_string());
let socket = Arc::new(UdpSocket::bind(&bind_addr).await?);
socket.set_broadcast(true)?;
let _announce_task = {
let socket = socket.clone();
let our_info = bincode::serialize(&self.session.our_info)
.map_err(|e| QsshError::Protocol(format!("Serialization failed: {}", e)))?;
tokio::spawn(async move {
loop {
if let Err(e) = socket.send_to(&our_info, "255.255.255.255:22223").await {
log::error!("Broadcast failed: {}", e);
}
tokio::time::sleep(Duration::from_secs(30)).await;
}
})
};
let mut buf = vec![0u8; 65536];
loop {
match socket.recv_from(&mut buf).await {
Ok((len, addr)) => {
if let Ok(peer_info) = bincode::deserialize::<PeerInfo>(&buf[..len]) {
if peer_info.id != self.session.our_info.id {
log::info!("Discovered peer {} at {}", peer_info.name, addr);
self.discovered_peers.write().await.push(peer_info);
}
}
}
Err(e) => log::error!("Discovery receive error: {}", e),
}
}
}
async fn dht_discovery(&self) -> Result<()> {
log::warn!("DHT discovery is a planned future enhancement");
Err(QsshError::Protocol("DHT discovery not yet available - use LocalBroadcast or Manual discovery".into()))
}
async fn blockchain_discovery(&self) -> Result<()> {
log::warn!("Blockchain discovery is a planned future enhancement");
Err(QsshError::Protocol("Blockchain discovery not yet available - use LocalBroadcast or Manual discovery".into()))
}
async fn manual_discovery(&self, peer_addrs: &[String]) -> Result<()> {
for addr in peer_addrs {
log::info!("Attempting to connect to manual peer: {}", addr);
match self.session.connect(addr).await {
Ok(conn) => {
log::info!("Connected to peer: {}", conn.peer_info().name);
}
Err(e) => {
log::warn!("Failed to connect to {}: {}", addr, e);
}
}
}
Ok(())
}
pub async fn get_peers(&self) -> Vec<PeerInfo> {
self.discovered_peers.read().await.clone()
}
}