diff --git a/src/discovery.rs b/src/discovery.rs new file mode 100644 index 0000000..c39e73b --- /dev/null +++ b/src/discovery.rs @@ -0,0 +1,49 @@ +use async_std::net::UdpSocket; +use async_std::sync::Arc; +use async_std::task; +use ed25519_dalek::PublicKey; + +use std::collections::HashSet; + +use crate::keypair::SSBPublicKey; +use crate::peer::{Address, Handshake, Peer, Protocol}; + +pub async fn recv(sender: async_std::channel::Sender) { + let socket = UdpSocket::bind(":::8008").await.unwrap(); + let mut buf = [0u8; 1024]; + let mut old = HashSet::new(); + + loop { + let (amt, _) = socket.recv_from(&mut buf).await.unwrap(); + let buf = &mut buf[..amt]; + let packet = String::from_utf8(buf.to_vec()).unwrap(); + let peer = Peer::from_discovery_packet(packet.as_str()); + if !old.contains(&peer) { + old.insert(peer.clone()); + sender.send(peer).await.unwrap(); + } + } +} + +pub async fn send(pubkey: Arc) { + let socket = UdpSocket::bind(":::0").await.unwrap(); + let msg = Peer::new( + Vec::from([Address::new( + Protocol::Net, + "1.2.3.4".parse().unwrap(), + 8023, + Handshake::Shs, + )]), + PublicKey::from_base64(pubkey.as_str()), + ) + .to_discovery_packet(); + let buf = msg.as_bytes(); + + socket.set_broadcast(true).unwrap(); + + loop { + socket.send_to(&buf, "255.255.255.255:8008").await.unwrap(); + socket.send_to(&buf, "[FF02::1]:8008").await.unwrap(); + task::sleep(std::time::Duration::from_secs(1)).await; + } +} diff --git a/src/main.rs b/src/main.rs index 499e929..8edb2aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,15 @@ -use async_std::{fs, task}; use async_std::path::PathBuf; use async_std::sync::Arc; +use async_std::{fs, task}; use clap::{load_yaml, App}; use ed25519_dalek::Keypair; +mod discovery; mod keypair; +mod peer; + use keypair::{SSBKeypair, SSBPublicKey}; -mod network; - -type Config = toml::map::Map; - #[async_std::main] async fn main() { let options = load_yaml!("options.yaml"); @@ -26,7 +25,7 @@ async fn main() { ), }; let config = fs::read_to_string(config_file).await.unwrap(); - let config: Config = toml::from_str(config.as_str()).unwrap(); + let config: toml::map::Map = toml::from_str(config.as_str()).unwrap(); let path = match options.value_of("path") { Some(path) => PathBuf::from(path), @@ -36,10 +35,12 @@ async fn main() { }, }; let keypair = Keypair::read_or_generate(path.join("secret")).await; - println!("{}", keypair.to_json().pretty(2)); - let pubkey = keypair.public.to_base64(); + let (psend, precv) = async_std::channel::unbounded(); + task::spawn(async move { discovery::recv(psend).await }); + task::spawn(discovery::send(Arc::new(keypair.public.to_base64()))); - task::spawn(network::peer_discovery_recv()); - task::spawn(network::peer_discovery_send(Arc::new(pubkey))).await; + while let Ok(peer) = precv.recv().await { + println!("{}", peer.to_discovery_packet()); + }; } diff --git a/src/network.rs b/src/network.rs deleted file mode 100644 index 230c8e7..0000000 --- a/src/network.rs +++ /dev/null @@ -1,93 +0,0 @@ -use async_std::task; -use async_std::net::{IpAddr, UdpSocket}; -use async_std::sync::Arc; -use ed25519_dalek::PublicKey; - -use crate::keypair::SSBPublicKey; - -enum Protocol { - Net, - Ws, - Wss, -} - -pub struct Peer { - protocol: Protocol, - host: IpAddr, - port: u16, - pubkey: PublicKey, -} - -impl Peer { - pub fn to_discovery_packet(&self) -> String { - let proto = match self.protocol { - Protocol::Net => "net", - Protocol::Ws => "ws", - Protocol::Wss => "wss", - }; - format!( - "{}:{}:{}~shs:{}", - proto, - self.host, - self.port, - self.pubkey.to_base64() - ) - } - - pub fn from_discovery_packet(packet: &str) -> Self { - let mut packet = packet.splitn(4, ':'); - let protocol = match packet.next().unwrap() { - "net" => Protocol::Net, - "ws" => Protocol::Ws, - "wss" => Protocol::Wss, - _ => panic!("unknown protocol"), - }; - let host = IpAddr::V4(packet.next().unwrap().parse().unwrap()); - let port = packet - .next() - .unwrap() - .splitn(2, '~') - .next() - .unwrap() - .parse() - .unwrap(); - let pubkey = SSBPublicKey::from_base64(packet.next().unwrap()); - Peer { - protocol, - host, - port, - pubkey, - } - } -} - -pub async fn peer_discovery_recv() { - let socket = UdpSocket::bind(":::8008").await.unwrap(); - let mut buf = [0u8; 1024]; - - loop { - let (amt, peer) = socket.recv_from(&mut buf).await.unwrap(); - let buf = &mut buf[..amt]; - let packet = String::from_utf8(buf.to_vec()).unwrap(); - println!( - "{} {}", - peer, - Peer::from_discovery_packet(&packet).to_discovery_packet() - ); - } -} - -pub async fn peer_discovery_send(pubkey: Arc) { - let socket = UdpSocket::bind(":::0").await.unwrap(); - let msg = format!("net:1.2.3.4:8023~shs:{}", &pubkey); - let buf = msg.as_bytes(); - - socket.set_broadcast(true).unwrap(); - - loop { - println!("Sending packet: {:?}", &msg); - socket.send_to(&buf, "255.255.255.255:8008").await.unwrap(); - socket.send_to(&buf, "[FF02::1]:8008").await.unwrap(); - task::sleep(std::time::Duration::from_secs(1)).await; - } -} diff --git a/src/peer.rs b/src/peer.rs new file mode 100644 index 0000000..621792e --- /dev/null +++ b/src/peer.rs @@ -0,0 +1,135 @@ +use async_std::net::IpAddr; +use ed25519_dalek::PublicKey; + +use std::hash::{Hash, Hasher}; + +use crate::keypair::SSBPublicKey; + +#[derive(Clone)] +pub enum Protocol { + Net, + Ws, + Wss, +} + +#[derive(Clone)] +pub enum Handshake { + Shs, + Shs2, +} + +#[derive(Clone)] +pub struct Address { + protocol: Protocol, + host: IpAddr, + port: u16, + handshake: Handshake, +} + +impl Address { + pub fn new(protocol: Protocol, host: IpAddr, port: u16, handshake: Handshake) -> Self { + Self { + protocol, + host, + port, + handshake, + } + } +} + +#[derive(Clone)] +pub struct Peer { + addresses: Vec
, + key: PublicKey, +} + +impl Peer { + pub fn new(addresses: Vec
, key: PublicKey) -> Self { + Self { addresses, key } + } + + // TODO: do this properly + pub fn to_discovery_packet(&self) -> String { + self.addresses + .iter() + .map(|address| { + let proto = match address.protocol { + Protocol::Net => "net", + Protocol::Ws => "ws", + Protocol::Wss => "wss", + }; + let hs = match address.handshake { + Handshake::Shs => "shs", + Handshake::Shs2 => "shs2", + }; + format!( + "{}:{}:{}~{}:{}", + proto, + address.host, + address.port, + hs, + self.key.to_base64(), + ) + }) + .collect::>() + .join(";") + } + + // TODO: do this properly + pub fn from_discovery_packet(packet: &str) -> Self { + let mut key = Option::None; + let addresses = packet + .split(';') + .map(|address| { + let mut address = address.splitn(2, '~'); + + let mut network = address.next().unwrap().splitn(3, ':'); + let protocol = match network.next().unwrap() { + "net" => Protocol::Net, + "ws" => Protocol::Ws, + "wss" => Protocol::Wss, + _ => panic!("unknown protocol"), + }; + let host = IpAddr::V4(network.next().unwrap().parse().unwrap()); + let port = network.next().unwrap().parse().unwrap(); + + let mut info = address.next().unwrap().splitn(2, ':'); + let handshake = match info.next().unwrap() { + "shs" => Handshake::Shs, + "shs2" => Handshake::Shs2, + _ => panic!("unknown handshake"), + }; + let pubkey = SSBPublicKey::from_base64(info.next().unwrap()); + if key == Option::None { + key = Some(pubkey); + } else if key.unwrap() != pubkey { + panic!("unexpected pubkey"); + } + + Address { + protocol, + host, + port, + handshake, + } + }) + .collect(); + Peer { + addresses, + key: key.unwrap(), + } + } +} + +impl Hash for Peer { + fn hash(&self, state: &mut H) { + self.key.to_bytes().hash(state); + } +} + +impl std::cmp::PartialEq for Peer { + fn eq(&self, other: &Self) -> bool { + self.key == other.key + } +} +impl std::cmp::Eq for Peer {}