diff --git a/client-gui/src/gui_app.rs b/client-gui/src/gui_app.rs index df02138..9b5fdc2 100644 --- a/client-gui/src/gui_app.rs +++ b/client-gui/src/gui_app.rs @@ -27,7 +27,7 @@ pub struct P2PClientApp { // GUI State status_message: String, - known_peers: Vec, + known_peers: Vec<(String, bool)>, connect_address_input: String, connected_address: String, connect_name_input: String, @@ -62,7 +62,7 @@ impl P2PClientApp { network_cmd_tx: cmd_tx, network_event_rx: event_rx, status_message: "Client Initialized. Awaiting network status...".to_string(), - known_peers: vec!["bob".to_string()], + known_peers: vec![("bob".to_string(), true)], connect_address_input: "https://jch.irif.fr:8443".to_string(), connected_address: "".to_string(), loaded_fs, @@ -111,8 +111,8 @@ impl eframe::App for P2PClientApp { todo!(); self.status_message = format!("✅ Peer connected: {}", addr); - if !self.known_peers.contains(&addr) { - self.known_peers.push(addr); + if !self.known_peers.contains(&(addr, true)) { + self.known_peers.push((addr, true)); } } NetworkEvent::PeerListUpdated(peers) => { @@ -343,18 +343,18 @@ impl eframe::App for P2PClientApp { } else { for peer in &self.known_peers { let is_active = - self.active_peer.as_ref().map_or(false, |id| id == peer); // if peer.id == self.active_peer_id + self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id let selectable; - if &self.active_server == peer { + if &self.active_server == &peer.0 { selectable = - ui.selectable_label(is_active, format!("{} 📡 🌀", peer)) + ui.selectable_label(is_active, format!("{} 📡 🌀", peer.0)) } else { - selectable = ui.selectable_label(is_active, format!("{}", peer)); + selectable = ui.selectable_label(is_active, format!("{}", peer.0)); } if selectable.clicked() { // switch to displaying this peer's tree - self.active_peer = Some(peer.clone()); + self.active_peer = Some(peer.0.clone()); // Request root content if not loaded if !self .loaded_fs @@ -375,10 +375,10 @@ impl eframe::App for P2PClientApp { .button("Utiliser le peer en tant que serveur") .clicked() { - self.active_server = peer.to_string(); + self.active_server = peer.0.to_string(); let res = self.network_cmd_tx.send( NetworkCommand::ServerHandshake( - peer.to_string(), + peer.0.to_string(), self.connected_address.clone(), ), ); diff --git a/client-network/src/lib.rs b/client-network/src/lib.rs index 7fe4742..3e41ef7 100644 --- a/client-network/src/lib.rs +++ b/client-network/src/lib.rs @@ -125,7 +125,7 @@ pub enum NetworkEvent { Disconnected(), Error(String), PeerConnected(String), - PeerListUpdated(Vec), + PeerListUpdated(Vec<(String, bool)>), FileTreeReceived(String, Vec), // peer_id, content DataReceived(String, MerkleNode), FileTreeRootReceived(String, String), @@ -295,11 +295,11 @@ pub fn start_p2p_executor( match get_peer_list(ip).await { Ok(body) => match String::from_utf8(body.to_vec()) { Ok(peers_list) => { - let mut peers: Vec = Vec::new(); + let mut peers: Vec<(String, bool)> = Vec::new(); let mut current = String::new(); for i in peers_list.chars() { if i == '\n' { - peers.push(current.clone()); + peers.push((current.clone(), false)); current.clear(); } else { current.push(i); diff --git a/client-network/src/messages_structure.rs b/client-network/src/messages_structure.rs index 2376e83..215ea7b 100644 --- a/client-network/src/messages_structure.rs +++ b/client-network/src/messages_structure.rs @@ -67,3 +67,176 @@ pub fn construct_message( } None } + +pub struct UDPMessage { + id: u32, + msg_type: u8, + length: u16, + body: Vec, + signature: Vec, +} + +pub struct HandshakeMessage { + pub id: u32, + msg_type: u8, + length: u16, + extensions: u32, + pub name: Vec, + pub signature: Vec, +} + +pub struct NatTraversal {} + +impl UDPMessage { + pub fn ping(id: u32) -> UDPMessage { + UDPMessage { + id: id, + msg_type: 0, + length: 0, + body: vec![0; 985], + signature: vec![0; 32], + } + } + + pub fn error(id: u32) -> UDPMessage { + UDPMessage { + id: id, + msg_type: 129, + length: 0, + body: vec![0; 985], + signature: vec![0; 32], + } + } + + pub fn parse(received_message: Vec) -> UDPMessage { + let id_bytes: [u8; 4] = received_message[0..4] + .try_into() + .expect("Taille incorrecte"); + let length_bytes: [u8; 2] = received_message[5..7] + .try_into() + .expect("Taille incorrecte"); + let msg_length = u16::from_be_bytes(length_bytes); + let name_bytes = &received_message[7..msg_length as usize + 8]; + let signature_bytes = + &received_message[msg_length as usize + 8..msg_length as usize + 9 + 32]; + UDPMessage { + id: u32::from_be_bytes(id_bytes), + msg_type: received_message[4], + length: u16::from_be_bytes(length_bytes), + body: name_bytes.to_vec(), + signature: signature_bytes.to_vec(), + } + } + + pub fn display(&self) { + println!("ID: {:?}", self.id); + println!("Message Type: {}", self.msg_type); + println!("Length: {:?}", self.length); + let good_length = usize::min(self.length as usize, 985); + println!("name: {:?}", &self.body[..good_length]); + println!("Signature: {:?}", self.signature); + } +} + +impl HandshakeMessage { + pub fn display(&self) { + println!("ID: {:?}", self.id); + println!("Message Type: {}", self.msg_type); + println!("Length: {:?}", self.length); + println!("extensions: {:?}", self.extensions); + println!("name: {:?}", &self.name[..(self.length - 4) as usize]); + println!("Signature: {:?}", self.signature); + } + pub fn hello(id: u32, length: u16, username: String) -> HandshakeMessage { + let name_vec = username.trim_end_matches(char::from(0)).as_bytes().to_vec(); + HandshakeMessage { + id: id, + msg_type: 1, + length: length, + extensions: 0, + name: name_vec, + signature: vec![0; 64], + } + } + + pub fn helloReply(id: u32, length: u16, username: String) -> HandshakeMessage { + let name_vec = username.trim_end_matches(char::from(0)).as_bytes().to_vec(); + HandshakeMessage { + id: id, + msg_type: 130, + length: length, + extensions: 0, + name: name_vec, + signature: vec![0; 64], + } + } + + pub fn serialize(&self) -> Vec { + let mut out = Vec::with_capacity(4 + 1 + 2 + 4 + self.name.len() + self.signature.len()); + + // id: u32 little-endian + out.extend_from_slice(&self.id.to_be_bytes()); + + // msg_type: u8 + out.push(self.msg_type); + + out.extend_from_slice(&self.length.to_be_bytes()); + + out.extend_from_slice(&self.extensions.to_be_bytes()); + + out.extend_from_slice(&self.name); + + out.extend_from_slice(&self.signature); + + out + } + + pub fn parse(received_message: Vec) -> HandshakeMessage { + let id_bytes: [u8; 4] = received_message[0..4] + .try_into() + .expect("Taille incorrecte"); + let length_bytes: [u8; 2] = received_message[5..7] + .try_into() + .expect("Taille incorrecte"); + let msg_length = u16::from_be_bytes(length_bytes); + let extensions_bytes: [u8; 4] = received_message[7..11] + .try_into() + .expect("Taille incorrecte"); + let name_bytes = &received_message[11..(11 + msg_length - 4) as usize]; + let signature_bytes = + &received_message[(11 + msg_length - 4) as usize..(11 + msg_length - 4 + 64) as usize]; + HandshakeMessage { + id: u32::from_be_bytes(id_bytes), + msg_type: received_message[4], + length: u16::from_be_bytes(length_bytes), + extensions: u32::from_be_bytes(extensions_bytes), + name: name_bytes.to_vec(), + signature: signature_bytes.to_vec(), + } + } +} + +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + /// creates an handshake message + #[tokio::test] + async fn creating_handshake_msg() { + let username = String::from("charlie_kirk"); + let handshake = HandshakeMessage::hello(0, 12, username); + handshake.display(); + } + + /// parses an handshake message + #[tokio::test] + async fn parse_handshakemessage() { + let username = String::from("charlie_kirk"); + let handshake = HandshakeMessage::hello(0, 12, username); + let ser = handshake.serialize(); + let parsed = HandshakeMessage::parse(ser); + handshake.display(); + parsed.display(); + } +} diff --git a/todo.md b/todo.md index 3931686..d5d7193 100644 --- a/todo.md +++ b/todo.md @@ -1,13 +1,32 @@ -# Todo : - +# Todo ## peer discovery ## handshake + +# Todo + +## peer discovery + +- get rsquest to the uri /peers/ + +## registration with the server + +- generation of the cryptographic key OK +- put request to the uri (check if the peer is already connected) OK +- udp handshakes OK +- get request to the uri /peers/key to get the public key of a peer OK +- get request to the uri /peers/key/addresses OK + +## handshake + +- handshake structure OK + - 5min timeout after handshake - matain connection every 4 min ## data transfer + - request structure - root/root reply structure - datum/nodatum and datum structures @@ -15,7 +34,16 @@ - setting in gui to act as a relay - chunk, directory, big, bigdirectory structures -## fonctionnalités application : +## fonctionnalités application + +## nat traversal + +- make hello and helloreply messages set the first extension bit to announce that peer is available for nat traversal +- implement actual nat traversal requests +- implement nat traversal : + - if hello/helloreply doesnt work with a peer, find a peer that supports nat traversal (server in priority) then begin protocol + +fonctionnalités : rechercher les fichiers d'un pair telechargement des fichiers @@ -25,16 +53,14 @@ choisir le nombre de canaux handshake server DOING se deconnecter du réseau DOING - -## autre : +## autre socket ipv6 - -# FAIT : +# FAIT - choisir un pseudo OK -- get rsquest to the uri /peers/ OK +- get rsquest to the uri /peers/ OK - generation of the cryptographic key OK - put request to the uri (check if the peer is already connected) OK - get request to the uri /peers/key to get the public key of a peer OK @@ -45,4 +71,3 @@ socket ipv6 - generer une clé publique OK - verifier signature OK - 2 channels -> un pour envoyer et un pour recevoir OK -