From a3648c2116825c66159b69308fdec07214704ece Mon Sep 17 00:00:00 2001 From: Tiago Batista Cardoso Date: Sat, 24 Jan 2026 19:47:15 +0100 Subject: [PATCH] working --- Download/12.text | Bin 0 -> 4 bytes Download/13.text | Bin 0 -> 4 bytes Download/README | Bin 0 -> 51 bytes client-gui/src/gui_app.rs | 49 +++++- client-network/src/fetchsocketaddresserror.rs | 26 +++ client-network/src/lib.rs | 162 ++++++++++++------ client-network/src/message_handling.rs | 51 +++++- client-network/src/messages_channels.rs | 2 + client-network/src/timestamp.rs | 46 +++++ 9 files changed, 270 insertions(+), 66 deletions(-) create mode 100644 Download/12.text create mode 100644 Download/13.text create mode 100644 Download/README create mode 100644 client-network/src/fetchsocketaddresserror.rs create mode 100644 client-network/src/timestamp.rs diff --git a/Download/12.text b/Download/12.text new file mode 100644 index 0000000000000000000000000000000000000000..45ba044c3840ab5567637e81e50aefa81e475543 GIT binary patch literal 4 LcmZQDG~xmP0R;eV literal 0 HcmV?d00001 diff --git a/Download/13.text b/Download/13.text new file mode 100644 index 0000000000000000000000000000000000000000..36bfdb0501469458ba9b508a3754ab7a7b299b61 GIT binary patch literal 4 LcmZQDH0A;T0S5qY literal 0 HcmV?d00001 diff --git a/Download/README b/Download/README new file mode 100644 index 0000000000000000000000000000000000000000..a3fb69c21205bdc0d14203fbe86e62529cc5a1c4 GIT binary patch literal 51 zcmZPwIJ`M4<>;=Xds1~2vNMmiD3lzTsgRtJTUwNte{}Ydc7<|<^rKBjT9XvgauSQH HD)qPkbE_8| literal 0 HcmV?d00001 diff --git a/client-gui/src/gui_app.rs b/client-gui/src/gui_app.rs index d279c07..12f123e 100644 --- a/client-gui/src/gui_app.rs +++ b/client-gui/src/gui_app.rs @@ -5,9 +5,8 @@ use client_network::{ }; use crossbeam_channel::{Receiver, Sender}; use egui::{ - Align, Align2, Button, CentralPanel, CollapsingHeader, Context, Id, LayerId, Layout, Order, - Popup, ScrollArea, SidePanel, TextStyle, TopBottomPanel, Ui, ViewportCommand, - debug_text::print, + CentralPanel, CollapsingHeader, Color32, Context, CornerRadius, Frame, Response, ScrollArea, + SidePanel, Stroke, TopBottomPanel, Ui, ViewportCommand, }; use std::collections::HashSet; use std::{collections::HashMap, fmt::format, io::Seek}; @@ -49,6 +48,7 @@ pub struct P2PClientApp { show_network_popup: bool, // gérer selon besoin error_message: Option, // Some(message) -> afficher, None -> rien + success_message: Option, // Some(message) -> afficher, None -> rien active_server: String, current_downloading_file_map: MerkleTree, @@ -72,7 +72,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(), true)], + known_peers: Vec::new(), connect_address_input: "https://jch.irif.fr:8443".to_string(), connected_address: "".to_string(), loaded_fs, @@ -80,6 +80,7 @@ impl P2PClientApp { server_status: ServerStatus::NotConnected, show_network_popup: false, error_message: None, + success_message: None, connect_name_input: "bob".to_string(), active_server: "".to_string(), shared_tree: generate_base_tree(), @@ -91,9 +92,15 @@ impl P2PClientApp { pub fn show_error(&mut self, msg: impl Into) { self.error_message = Some(msg.into()); } + pub fn show_success(&mut self, msg: impl Into) { + self.success_message = Some(msg.into()); + } pub fn clear_error(&mut self) { self.error_message = None; } + pub fn clear_success(&mut self) { + self.success_message = None; + } } // --- eframe::App Trait Implementation --- @@ -292,6 +299,9 @@ impl eframe::App for P2PClientApp { println!("bigfile téléchargé"); } } + NetworkEvent::Success(msg) => { + self.show_success(msg); + } NetworkEvent::HandshakeFailed() => {} NetworkEvent::ServerHandshakeFailed(err) => { self.active_server = "".to_string(); @@ -424,13 +434,24 @@ impl eframe::App for P2PClientApp { let is_active = self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id - let selectable; + let selectable: Response; if &self.active_server == &peer.0 { - selectable = - ui.selectable_label(is_active, format!("{} 📡 🌀", peer.0)) + // Create a frame with green background and render the selectable inside it. + // Adjust rounding, padding and stroke as desired. + let frame = Frame { + fill: Color32::DARK_BLUE, + stroke: Stroke::default(), + corner_radius: CornerRadius::from(0.5), + ..Default::default() + }; + let internal = frame.show(ui, |ui| { + ui.selectable_label(is_active, format!("{}", peer.0)) + }); + selectable = internal.inner; } else { selectable = ui.selectable_label(is_active, format!("{}", peer.0)); } + if selectable.clicked() { // switch to displaying this peer's tree self.active_peer = Some(peer.0.clone()); @@ -540,6 +561,20 @@ impl eframe::App for P2PClientApp { }); ctx.request_repaint(); } + if let Some(msg) = &self.success_message { + let msg = msg.clone(); + egui::Window::new("Success") + .collapsible(false) + .resizable(false) + .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) + .show(ctx, |ui| { + ui.label(&msg); + if ui.button("OK").clicked() { + self.clear_success(); + } + }); + ctx.request_repaint(); + } ctx.request_repaint_after(std::time::Duration::from_millis(10)); } diff --git a/client-network/src/fetchsocketaddresserror.rs b/client-network/src/fetchsocketaddresserror.rs new file mode 100644 index 0000000..66c507f --- /dev/null +++ b/client-network/src/fetchsocketaddresserror.rs @@ -0,0 +1,26 @@ +use std::fmt; + +#[derive(Debug)] +pub enum FetchSocketAddressError { + NoIPV4Address, + NoRegisteredAddresses, + NoResponseFromUser, + ClientError(String), +} + +impl fmt::Display for FetchSocketAddressError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FetchSocketAddressError::NoIPV4Address => write!(f, "No IPv4 Address registered."), + FetchSocketAddressError::NoRegisteredAddresses => { + write!(f, "No Registered Addresses found.") + } + FetchSocketAddressError::NoResponseFromUser => { + write!(f, "No Response from user after contact.") + } + FetchSocketAddressError::ClientError(error) => { + write!(f, "Client error : {}", error) + } + } + } +} diff --git a/client-network/src/lib.rs b/client-network/src/lib.rs index 2154abf..aca1065 100644 --- a/client-network/src/lib.rs +++ b/client-network/src/lib.rs @@ -2,6 +2,7 @@ mod cryptographic_signature; mod data; mod datum_generation; mod datum_parsing; +mod fetchsocketaddresserror; mod message_handling; mod messages_channels; mod messages_structure; @@ -9,9 +10,12 @@ mod peers_refresh; mod registration; mod server_communication; mod threads_handling; +mod timestamp; +use crate::fetchsocketaddresserror::FetchSocketAddressError; use crate::messages_structure::ROOTREPLY; use crate::peers_refresh::*; +use crate::timestamp::Timestamp; use crate::{ cryptographic_signature::CryptographicSignature, message_handling::EventType, @@ -26,7 +30,6 @@ use crate::{ threads_handling::Worker, }; use std::collections::HashSet; - use std::{ io::Error, net::{IpAddr, Ipv4Addr, UdpSocket}, @@ -41,6 +44,7 @@ pub struct P2PSharedData { shared_socket: Arc, shared_cryptopair: Arc, shared_messageslist: Arc>>, + shared_messagesreceived: Arc>>, shared_senders: Arc, server_name: Arc>, server_address: Arc>, @@ -50,8 +54,7 @@ pub struct P2PSharedData { use bytes::Bytes; use reqwest::Client; -use tokio::sync::oneshot; -use tokio::time::{sleep, timeout}; +use tokio::time::sleep; impl P2PSharedData { pub fn new( @@ -59,12 +62,14 @@ impl P2PSharedData { cmd_tx: crossbeam_channel::Sender, ) -> Result { let messages_list = HashMap::::new(); + let messagesrecv_list = HashMap::::new(); let username = String::from(username); let crypto_pair = CryptographicSignature::new(username); let socket = UdpSocket::bind("0.0.0.0:0")?; let shared_socket = Arc::new(socket); let shared_cryptopair = Arc::new(crypto_pair); let shared_messageslist = Arc::new(Mutex::new(messages_list)); + let shared_messagesreceived = Arc::new(Mutex::new(messagesrecv_list)); let mut threads = Vec::new(); @@ -78,6 +83,7 @@ impl P2PSharedData { shared_socket: shared_socket, shared_cryptopair: shared_cryptopair, shared_messageslist: shared_messageslist, + shared_messagesreceived: shared_messagesreceived, shared_senders: shared_senders, server_name: server_name, server_address: server_address, @@ -95,12 +101,21 @@ impl P2PSharedData { pub fn messages_list(&self) -> Arc>> { self.shared_messageslist.clone() } + pub fn messages_received(&self) -> Arc>> { + self.shared_messagesreceived.clone() + } pub fn servername(&self) -> String { - let guard = self.server_name.lock().unwrap(); + let guard = { + let maybe_sn = self.server_name.lock().unwrap(); + maybe_sn.clone() + }; guard.to_string() } pub fn serveraddress(&self) -> String { - let guard = self.server_address.lock().unwrap(); + let guard = { + let maybe_sn = self.server_address.lock().unwrap(); + maybe_sn.clone() + }; guard.to_string() } pub fn set_servername(&self, new: String) { @@ -128,6 +143,10 @@ impl P2PSharedData { &*self.shared_messageslist } + pub fn messages_received_ref(&self) -> &Mutex> { + &*self.shared_messagesreceived + } + pub fn senders_ref(&self) -> &MultipleSenders { &*self.shared_senders } @@ -176,6 +195,7 @@ pub enum NetworkEvent { ConnectedHandshake(), Disconnected(), Error(String), + Success(String), PeerConnected(String), PeerListUpdated(Vec<(String, bool)>), FileTreeReceived([u8; 32], MerkleNode, String), // peer_id, content @@ -468,6 +488,7 @@ pub fn start_p2p_executor( }*/ } NetworkCommand::FetchPeerList(ip) => { + println!("[Network] FetchPeerList() called"); if ip == "" { let res = event_tx.send(NetworkEvent::Error( "Not registered to any server".to_string(), @@ -497,12 +518,12 @@ pub fn start_p2p_executor( Err(e) => println!("error"), } } - println!("[Network] FetchPeerList() called"); } NetworkCommand::RegisterAsPeer(_) => { println!("[Network] RegisterAsPeer() called"); } NetworkCommand::Ping(str, ip) => { + println!("[Network] Ping({}) called", str); if let Some(sd) = shared_data.as_ref() { let id = generate_id(); sd.add_message(id, EventType::Ping); @@ -511,35 +532,51 @@ pub fn start_p2p_executor( let peer_address = get_socket_address(str, ip, shared_data.as_ref()).await; match peer_address { - Some(addr) => { - if let Some(ping) = pingrequest { - sd.senders_ref().add_message_to_retry_queue( - ping.clone(), - addr.to_string(), - false, - ); - sd.senders_ref().send_dispatch( - ping, - addr.to_string(), - false, - sd.messages_list(), - ); - } + Ok(addr) => { + //if let Some(ping) = pingrequest { + // sd.senders_ref().add_message_to_retry_queue( + // ping.clone(), + // addr.to_string(), + // false, + // ); + // sd.senders_ref().send_dispatch( + // ping, + // addr.to_string(), + // false, + // sd.messages_list(), + // ); + //} + match event_tx.send(NetworkEvent::Success(format!( + "Successfully sent ping message to {}.", + addr.to_string() + ))) { + Ok(_) => {} + Err(e) => { + eprintln!("NetworkEvent error : {}", e); + } + }; } - None => { - let err_msg = - format!("failed to retreive socket address:").to_string(); - let res = event_tx.send(NetworkEvent::Error(err_msg)); + Err(err_msg) => { + match event_tx.send(NetworkEvent::Error(err_msg.to_string())) { + Ok(_) => {} + Err(e) => { + eprintln!("NetworkEvent error : {}", e); + } + } } } } - println!("[Network] Ping() called"); } NetworkCommand::Disconnect() => { if let Some(sd) = shared_data.as_ref() { println!("Disconnecting: {}", &sd.cryptopair().username); shared_data = None; - let res = event_tx.send(NetworkEvent::Disconnected()); + match event_tx.send(NetworkEvent::Disconnected()) { + Ok(_) => {} + Err(e) => { + eprintln!("NetworkEvent error : {}", e); + } + } } else { println!("no p2p data"); } @@ -563,7 +600,7 @@ pub fn start_p2p_executor( ); match peer_addr_query.await { - Some(peer_addr) => { + Ok(peer_addr) => { let payload = socket_addr_to_vec(peer_addr); print!("{:?}", payload.clone()); @@ -586,10 +623,13 @@ pub fn start_p2p_executor( sd.messages_list(), ); } - None => { - let err_msg = - format!("failed to retreive socket address").to_string(); - let res = event_tx.send(NetworkEvent::Error(err_msg)); + Err(err_msg) => { + match event_tx.send(NetworkEvent::Error(err_msg.to_string())) { + Ok(_) => {} + Err(e) => { + eprintln!("NetworkEvent error : {}", e); + } + } } } } @@ -603,7 +643,7 @@ pub fn start_p2p_executor( // event_tx.send(NetworkEvent::PeerConnected("NewPeerID".to_string())).unwrap(); // Avoid spinning too fast - tokio::time::sleep(std::time::Duration::from_millis(50)).await; + sleep(std::time::Duration::from_millis(50)).await; } }) } @@ -634,7 +674,7 @@ fn parse_pack(s: &str) -> Option<[u8; 6]> { } async fn quick_ping(addr: &SocketAddr, timeout_ms: u64, sd: &P2PSharedData) -> bool { - let id = 42069; + let id = generate_id(); let pingreq = construct_message(PING, Vec::new(), id, &sd.shared_cryptopair); if let Some(ping) = pingreq { @@ -645,7 +685,15 @@ async fn quick_ping(addr: &SocketAddr, timeout_ms: u64, sd: &P2PSharedData) -> b sleep(Duration::from_millis(timeout_ms)).await; - !sd.messages_list().lock().expect("yooo").contains_key(&id) + let msg_list = sd.messages_list_ref().lock().expect("yooo"); + let res = !msg_list.contains_key(&id); + + for (id, evt) in msg_list.iter() { + println!("id : {}, evt : {}", id, evt.to_string()); + } + println!("message list doesnt contain key? {}", res); + + res } /// @@ -656,24 +704,20 @@ pub async fn get_socket_address( username: String, ip: String, shared_data: Option<&P2PSharedData>, -) -> Option { +) -> Result { let sd = shared_data.expect("No shared data"); let client = match Client::builder().timeout(Duration::from_secs(5)).build() { Ok(c) => c, Err(e) => { - eprintln!("Failed to build HTTP client: {}", e); - return None; + return Err(FetchSocketAddressError::ClientError(e.to_string())); } }; let uri = format!("{}/peers/{}/addresses", ip, username); let res = match client.get(&uri).send().await { Ok(r) => r, - Err(e) => { - eprintln!("HTTP request error: {}", e); - return None; - } + Err(e) => return Err(FetchSocketAddressError::ClientError(e.to_string())), }; if res.status().is_success() { @@ -688,29 +732,30 @@ pub async fn get_socket_address( let body = match res.bytes().await { Ok(b) => b, Err(e) => { - eprintln!("Failed to read response body: {}", e); - return None; + return Err(FetchSocketAddressError::ClientError(e.to_string())); } }; let s = match String::from_utf8(body.to_vec()) { Ok(st) => st, Err(e) => { - eprintln!("Response not valid UTF-8: {}", e); - return None; + return Err(FetchSocketAddressError::ClientError(e.to_string())); } }; let addresses = parse_addresses(&s); // assumes parse_addresses: &str -> Vec + if addresses.is_empty() { + return Err(FetchSocketAddressError::NoRegisteredAddresses); + } else if !addresses.iter().any(|a| matches!(a, SocketAddr::V4(_))) { + return Err(FetchSocketAddressError::NoIPV4Address); + } for addr in addresses { println!("trying address : {}", addr); - if quick_ping(&addr, 10000, sd).await { - return Some(addr); + if quick_ping(&addr, 5000, sd).await { + return Ok(addr); } - // TODO: nat_traversal(&addr).await; - // after NAT traversal attempt, ping again let payload = socket_addr_to_vec(addr); let id = generate_id(); @@ -724,12 +769,27 @@ pub async fn get_socket_address( sd.messages_list(), ); + sleep(Duration::from_millis(5000)).await; + + let maybe_entry = { + let guard = sd.messages_received_ref().lock().unwrap(); + guard.clone() + }; // guard dropped + + for (id, (evt, time)) in maybe_entry.iter() { + println!("{} : {} at {}", id, evt.to_string(), time.to_string()); + if id.eq(&addr.to_string()) && Timestamp::now().diff(time) < 10 { + println!("received message from address, returning said address.."); + return Ok(addr); + } + } + if quick_ping(&addr, 15000, sd).await { - return Some(addr); + return Ok(addr); } } - None + Err(FetchSocketAddressError::NoResponseFromUser) } pub async fn get_server_address(username: String, ip: String) -> Option { diff --git a/client-network/src/message_handling.rs b/client-network/src/message_handling.rs index 2f0e2b1..11842a2 100644 --- a/client-network/src/message_handling.rs +++ b/client-network/src/message_handling.rs @@ -1,19 +1,16 @@ -use tokio::sync::oneshot; - use crate::{ - NetworkEvent, NodeHash, P2PSharedData, - cryptographic_signature::{ - CryptographicSignature, get_peer_key, sign_message, verify_signature, - }, + NetworkEvent, NodeHash, + cryptographic_signature::{CryptographicSignature, get_peer_key, verify_signature}, datum_parsing::parse_received_datum, messages_channels::MultipleSenders, messages_structure::construct_message, peers_refresh::HandshakeHistory, - registration, server_communication::generate_id, + timestamp::Timestamp, }; use std::{ collections::HashMap, + default, net::{Ipv4Addr, SocketAddr}, }; use std::{ @@ -22,7 +19,7 @@ use std::{ }; // Types of messages that await for a response -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum EventType { HelloThenRootRequest, Hello, @@ -31,6 +28,33 @@ pub enum EventType { NatTraversal, DatumRequest, DatumRequestBig, + Unknown, +} + +impl EventType { + pub fn to_string(&self) -> String { + match self { + EventType::HelloThenRootRequest => "HelloThenRootRequest".to_owned(), + EventType::Hello => "Hello".to_owned(), + EventType::RootRequest => "RootRequest".to_owned(), + EventType::Ping => "Ping".to_owned(), + EventType::NatTraversal => "NatTraversal".to_owned(), + EventType::DatumRequest => "DatumRequest".to_owned(), + EventType::Unknown => "Unknown".to_owned(), + EventType::DatumRequestBig => "DatumRequestBig".to_owned(), + } + } + + pub fn from_msgtype(msgtype: u8) -> EventType { + match msgtype { + PING => EventType::Ping, + HELLO => EventType::Hello, + ROOTREQUEST => EventType::RootRequest, + NATTRAVERSALREQUEST => EventType::NatTraversal, + DATUMREQUEST => EventType::DatumRequest, + _ => EventType::Unknown, + } + } } const ID: usize = 4; @@ -54,6 +78,7 @@ const NATTRAVERSALREQUEST2: u8 = 5; pub fn handle_recevied_message( messages_list: &Arc>>, + messages_received: &Arc>>, recevied_message: &Vec, crypto_pair: &CryptographicSignature, //socket_addr: &SocketAddr, @@ -92,6 +117,7 @@ pub fn handle_recevied_message( cmd_tx, ip, messages_list, + messages_received, handhsake_history, senders, ); @@ -117,6 +143,7 @@ pub fn parse_message( cmd_tx: crossbeam_channel::Sender, ip: SocketAddr, messages_list: &Arc>>, + messages_received: &Arc>>, handhsake_history: Arc, senders: &MultipleSenders, ) -> Option> { @@ -128,6 +155,14 @@ pub fn parse_message( let msgtype = received_message[ID]; + messages_received + .lock() + .expect("couldnt lock received map") + .insert( + ip.to_string(), + (EventType::from_msgtype(msgtype), Timestamp::now()), + ); + let length_bytes: [u8; 2] = received_message[TYPE..LENGTH] .try_into() .expect("Taille incorrecte"); diff --git a/client-network/src/messages_channels.rs b/client-network/src/messages_channels.rs index b881c1f..9dd6285 100644 --- a/client-network/src/messages_channels.rs +++ b/client-network/src/messages_channels.rs @@ -269,6 +269,7 @@ pub fn start_receving_thread( let cryptopair_clone = shared_data.cryptopair(); let senders_clone = shared_data.senders(); let messages_clone = shared_data.messages_list(); + let messages_received_clone = shared_data.messages_received(); let servername_clone = shared_data.servername(); let thread = thread::spawn(move || { let mut buf = [0u8; 1024]; @@ -280,6 +281,7 @@ pub fn start_receving_thread( println!("Reçu {} octets de {}: {:?}", amt, src, received_data); handle_recevied_message( &messages_clone, + &messages_received_clone, &received_data, &cryptopair_clone, &senders_clone, diff --git a/client-network/src/timestamp.rs b/client-network/src/timestamp.rs new file mode 100644 index 0000000..45ce169 --- /dev/null +++ b/client-network/src/timestamp.rs @@ -0,0 +1,46 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Timestamp { + secs: u64, // seconds since UNIX epoch +} + +unsafe impl Send for Timestamp {} + +impl Timestamp { + // Create a Timestamp from current system time + pub fn now() -> Self { + let dur = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time before UNIX_EPOCH"); + Self { + secs: dur.as_secs(), + } + } + + // Create from explicit fields (optional helper) + pub fn from_secs(secs: u64) -> Self { + Self { secs } + } + + // Return underlying seconds + pub fn as_secs(&self) -> u64 { + self.secs + } + + // Return elapsed seconds between `self` and `other`. + // Panics if `other` is in the future relative to `self`. + // If you call `Timestamp::now().diff(past)`, it returns seconds since `past`. + pub fn diff(&self, earlier: &Timestamp) -> u64 { + assert!(earlier.secs <= self.secs, "given time is in the future"); + self.secs - earlier.secs + } + + pub fn to_string(&self) -> String { + let secs_of_day = self.secs % 86_400; + let hh = secs_of_day / 3600; + let mm = (secs_of_day % 3600) / 60; + let ss = secs_of_day % 60; + format!("{:02}:{:02}:{:02}", hh, mm, ss) + } +}