feat: add logging support

This commit is contained in:
Michel de Graaf
2025-07-05 23:16:05 +02:00
parent 57dafaf8ab
commit 956cb824bd
28 changed files with 765 additions and 203 deletions
+115 -13
View File
@@ -1,7 +1,7 @@
use crate::{
peer::{DefaultPeer, Peer},
server::{PeerAddress, Server},
types::FileSearchResult,
peer::{ConnectionType, DefaultPeer, Peer},
server::{PeerAddress, Server, ServerOperation},
types::{FileSearchResult, Transfer},
utils::{md5, thread_pool::ThreadPool},
};
use std::{
@@ -22,11 +22,15 @@ pub enum ClientOperation {
ConnectToPeer(Peer),
SearchResult(FileSearchResult),
PeerDisconnected(String),
TransferRequest(Transfer),
PierceFireWall(Peer),
}
struct ClientContext {
peers: HashMap<String, DefaultPeer>,
sender: Option<Sender<ClientOperation>>,
server_sender: Option<Sender<crate::server::ServerOperation>>,
search_results: Vec<FileSearchResult>,
downloads: HashMap<u32, Transfer>,
thread_pool: ThreadPool,
}
impl ClientContext {
@@ -34,7 +38,9 @@ impl ClientContext {
Self {
peers: HashMap::new(),
sender: None,
server_sender: None,
search_results: Vec::new(),
downloads: HashMap::new(),
thread_pool: ThreadPool::new(MAX_THREADS),
}
}
@@ -49,6 +55,7 @@ pub struct Client {
impl Client {
pub fn new(address: PeerAddress, username: String, password: String) -> Self {
crate::utils::logger::init();
Self {
address,
username,
@@ -67,17 +74,20 @@ impl Client {
// self.read_form_channel(message_reader);
self.server = match Server::new(self.address.clone(), sender) {
Ok(server) => {
println!(
info!(
"Connected to server at {}:{}",
server.get_address().get_host(),
server.get_address().get_port()
);
// Store the server sender in the client context
self.context.lock().unwrap().server_sender = Some(server.get_sender().clone());
Self::listen_to_client_operations(message_reader, self.context.clone());
Some(server)
}
Err(e) => {
eprintln!("Error connecting to server: {}", e);
error!("Error connecting to server: {}", e);
None
}
};
@@ -85,7 +95,7 @@ impl Client {
pub fn login(&self) -> Result<bool, std::io::Error> {
// Attempt to login
println!("Logging in as {}", self.username);
info!("Logging in as {}", self.username);
if let Some(server) = &self.server {
let result = server.login(&self.username, &self.password);
if result.unwrap() {
@@ -108,18 +118,18 @@ impl Client {
pub fn remove_peer(&self, username: &str) {
let mut context = self.context.lock().unwrap();
if let Some(peer) = context.peers.remove(username) {
drop(peer); // Explicitly drop to trigger cleanup
drop(peer);
}
}
pub fn search(&self, query: &str, timeout: Duration) -> Vec<FileSearchResult> {
println!("Searching for {}", query);
info!("Searching for {}", query);
if let Some(server) = &self.server {
let hash = md5::md5(query);
let token = i32::from_str_radix(&hash[0..5], 16).unwrap();
let token = u32::from_str_radix(&hash[0..5], 16).unwrap();
server.file_search(token, query);
} else {
eprintln!("Not connected to server");
warn!("Not connected to server");
}
let start = Instant::now();
@@ -131,6 +141,52 @@ impl Client {
return self.context.lock().unwrap().search_results.clone();
}
pub fn download(&self, filename: String, username: String) -> crate::types::DownloadResult {
use crate::types::{DownloadResult, DownloadStatus};
use std::time::{Duration, Instant};
println!("Downloading {} from {}", filename, username);
let start_time = Instant::now();
// Start the download request
let context = self.context.lock().unwrap();
let download_initiated = context
.peers
.get(&username)
.map(|p| p.transfer_request(filename.clone()))
.is_some();
drop(context);
let timeout = Duration::from_secs(50);
let check_interval = Duration::from_millis(100);
if !download_initiated {
return DownloadResult {
filename,
username,
status: DownloadStatus::Failed,
elapsed_time: start_time.elapsed(),
};
}
// Non-blocking wait loop
while start_time.elapsed() < timeout {
// Check download status (for now just wait, actual download logic will be implemented later)
std::thread::sleep(check_interval);
// TODO: Check actual download progress here
// For now, we'll just wait for the timeout
}
DownloadResult {
filename,
username,
status: DownloadStatus::TimedOut,
elapsed_time: start_time.elapsed(),
}
}
fn listen_to_client_operations(
reader: Receiver<ClientOperation>,
client_context: Arc<Mutex<ClientContext>>,
@@ -139,6 +195,14 @@ impl Client {
if let Ok(operation) = reader.recv() {
match operation {
ClientOperation::ConnectToPeer(peer) => {
match peer.connection_type {
ConnectionType::P => (),
ConnectionType::F => {
debug!("Peer with F {:?}", peer)
}
ConnectionType::D => (),
};
Self::connect_to_peer(peer, client_context.clone())
}
ClientOperation::SearchResult(file_search) => {
@@ -154,6 +218,16 @@ impl Client {
drop(peer); // Explicitly drop to trigger cleanup
}
}
ClientOperation::TransferRequest(transfer) => {
client_context
.lock()
.unwrap()
.downloads
.insert(transfer.token, transfer);
}
ClientOperation::PierceFireWall(peer) => {
Self::pierce_firewall(peer, client_context.clone());
}
}
}
});
@@ -174,14 +248,42 @@ impl Client {
let mut context = client_context.lock().unwrap();
context.peers.insert(peer.username, p);
}
Err(_e) => {
// eprintln!("Error connecting to peer: {:?}", e)
Err(e) => {
trace!(
"Can't connect to {} {}:{} {:?} - {}",
peer.username,
peer.host,
peer.port,
peer.connection_type,
e
);
}
}
});
}
} else {
eprintln!("No sender found");
error!("No sender found");
}
}
fn pierce_firewall(peer: Peer, client_context: Arc<Mutex<ClientContext>>) {
debug!("Piercing firewall for peer: {:?}", peer);
let context = client_context.lock().unwrap();
if let Some(server_sender) = &context.server_sender {
if let Some(token) = peer.token {
match server_sender.send(ServerOperation::PierceFirewall(token)) {
Ok(_) => debug!("Sent PierceFirewall message with token: {}", token),
Err(e) => error!("Failed to send PierceFirewall message: {}", e),
}
} else {
error!("No token available for PierceFirewall");
}
} else {
error!("No server sender available for PierceFirewall");
}
drop(context);
// Also try to connect to the peer directly
Self::connect_to_peer(peer, client_context);
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ impl<Op> MessageDispatcher<Op> {
message.set_pointer(8);
handler.handle(message, self.sender.clone());
} else {
println!(
warn!(
"No handler found for message code: {:?}",
message.get_message_code()
);
+10 -4
View File
@@ -1,14 +1,15 @@
#[macro_use]
mod utils;
mod client;
mod dispatcher;
mod message;
mod peer;
mod server;
mod types;
mod utils;
use client::Client;
use server::PeerAddress;
use std::time::Duration;
use std::time::{Duration, Instant};
// PeerAddress::new(String::from("server.slsknet.org"), 2416),
fn main() {
@@ -25,10 +26,15 @@ fn main() {
match client.login() {
Ok(_) => {
let results = client.search("Fantazia", Duration::from_secs(10));
println!("Search results: {:?}", results);
println!("Search results: {} - {:?} ", results.len(), results);
if !results.is_empty() && !results[0].files.is_empty() {
let file = results[0].files[0].clone();
let download_result = client.download(file.name, file.username);
println!("Download result: {:?}", download_result);
}
}
Err(e) => {
println!("Failed to login: {}", e);
error!("Failed to login: {}", e);
}
}
}
View File
+102 -23
View File
@@ -8,6 +8,25 @@ pub use message_reader::*;
use std::str;
#[derive(Debug, PartialEq)]
pub enum MessageType {
Server,
Peer,
PeerInit,
Distributed,
}
#[derive(Debug)]
pub struct Error(String);
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for Error {}
#[derive(Debug, PartialEq, Clone)]
pub struct Message {
data: Vec<u8>,
@@ -62,7 +81,7 @@ impl Message {
}
}
println!();
trace!("");
}
}
//
@@ -122,9 +141,9 @@ impl Message {
pub fn new_with_data(data: Vec<u8>) -> Self {
Self { data, pointer: 0 }
}
// pub fn reset_pointer(&mut self) {
// self.pointer = 0;
// }
pub fn reset_pointer(&mut self) {
self.pointer = 0;
}
pub fn set_pointer(&mut self, pointer: usize) {
self.pointer = pointer;
}
@@ -169,27 +188,16 @@ impl Message {
String::from_utf8(data.to_vec()).expect("Failed to read string")
}
pub fn read_raw_hex_str(&mut self, size: usize) -> String {
let hex_str = self.data[self.pointer..self.pointer + size]
.iter()
.fold(String::new(), |mut acc, byte| {
use std::fmt::Write;
write!(acc, "{:02x}", byte).expect("Failed to write to string");
acc
});
self.pointer += size;
hex_str
}
pub fn read_int8(&mut self) -> i8 {
let val = self.data[self.pointer] as i8;
pub fn read_int8(&mut self) -> u8 {
let val = self.data[self.pointer];
self.pointer += 1;
val
}
#[allow(dead_code)]
pub fn read_int64(&mut self) -> i64 {
let val = i64::from_le_bytes([
pub fn read_int64(&mut self) -> u64 {
let val = u64::from_le_bytes([
self.data[self.pointer],
self.data[self.pointer + 1],
self.data[self.pointer + 2],
@@ -203,12 +211,12 @@ impl Message {
val
}
pub fn read_int32(&mut self) -> i32 {
pub fn read_int32(&mut self) -> u32 {
if self.pointer + 4 > self.data.len() {
return 0;
}
let val = i32::from_le_bytes([
let val = u32::from_le_bytes([
self.data[self.pointer],
self.data[self.pointer + 1],
self.data[self.pointer + 2],
@@ -231,7 +239,12 @@ impl Message {
self
}
pub fn write_int32(&mut self, value: i32) -> &mut Self {
pub fn write_int32(&mut self, value: u32) -> &mut Self {
self.data.extend_from_slice(&value.to_le_bytes());
self
}
pub fn write_int64(&mut self, value: u64) -> &mut Self {
self.data.extend_from_slice(&value.to_le_bytes());
self
}
@@ -240,6 +253,11 @@ impl Message {
self
}
pub fn write_bool(&mut self, value: bool) -> &mut Self {
self.data.push(if value { 1 } else { 0 });
self
}
pub fn write_raw_hex_string(&mut self, val: &str) -> &mut Self {
let mut b = Vec::new();
for i in (0..val.len()).step_by(2) {
@@ -251,6 +269,67 @@ impl Message {
self.pointer += b.len();
self
}
pub fn get_message_name(&self, msg_type: MessageType, code: u32) -> Result<&str, Error> {
match msg_type {
MessageType::Server => match code {
1 => Ok("Login"),
2 => Ok("SetWaitPort"),
3 => Ok("GetPeerAddress"),
5 => Ok("WatchUser"),
6 => Ok("UnwatchUser"),
7 => Ok("GetUserStatus"),
13 => Ok("SayChatroom"),
14 => Ok("JoinRoom"),
15 => Ok("LeaveRoom"),
18 => Ok("ConnectToPeer"),
22 => Ok("MessageUser"),
23 => Ok("MessageAcked"),
26 => Ok("FileSearch"),
28 => Ok("SetStatus"),
32 => Ok("ServerPing"),
35 => Ok("SharedFoldersFiles"),
36 => Ok("GetUserStats"),
41 => Ok("Relogged"),
42 => Ok("UserSearch"),
64 => Ok("RoomList"),
92 => Ok("CheckPrivileges"),
100 => Ok("AcceptChildren"),
102 => Ok("PossibleParents"),
1001 => Ok("CantConnectToPeer"),
_ => Err(Error(format!("Unknown server message code: {}", code))),
},
MessageType::PeerInit => match code {
0 => Ok("PierceFireWall"),
1 => Ok("PeerInit"),
_ => Err(Error(format!("Unknown peer init message code: {}", code))),
},
MessageType::Peer => match code {
4 => Ok("GetShareFileList"),
5 => Ok("SharedFileListResponse"),
9 => Ok("FileSearchResponse"),
15 => Ok("UserInfoRequest"),
16 => Ok("UserInfoResponse"),
36 => Ok("FolderContentsRequest"),
37 => Ok("FolderContentsResponse"),
40 => Ok("TransferRequest"),
41 => Ok("TransferResponse"),
43 => Ok("QueueUpload"),
44 => Ok("PlaceInQueueResponse"),
46 => Ok("UploadFailed"),
50 => Ok("UploadDenied"),
51 => Ok("PlaceInQueueRequest"),
_ => Err(Error(format!("Unknown peer message code: {}", code))),
},
MessageType::Distributed => match code {
3 => Ok("SearchRequest"),
4 => Ok("BranchLevel"),
5 => Ok("BranchRoot"),
93 => Ok("EmbeddedMessage"),
_ => Err(Error(format!("Unknown distributed message code: {}", code))),
},
}
}
// pub fn decode(&self) {
// println!("{:?}", self.data);
// }
@@ -326,4 +405,4 @@ fn test_read_string_invalid_utf8() {
];
let mut test_data = Message::new_with_data(data);
test_data.read_string();
}
}
+2 -49
View File
@@ -1,55 +1,8 @@
use crate::message::{Message, MessageHandler};
use crate::peer::PeerOperation;
use crate::types::{File, FileSearchResult};
use crate::utils::zlib::deflate;
use std::collections::HashMap;
use crate::types::FileSearchResult;
use std::sync::mpsc::Sender;
fn decompress_buffer(buffer: &[u8]) -> Result<Vec<u8>, String> {
deflate(buffer)
}
impl FileSearchResult {
pub fn new_from_message(message: &mut Message) -> Self {
let pointer = message.get_pointer();
let size = message.get_size();
let data: Vec<u8> = message.get_slice(pointer, size);
let deflated = decompress_buffer(&data).unwrap();
let mut message = Message::new_with_data(deflated);
let username = message.read_string();
let token = message.read_raw_hex_str(4);
let n_files = message.read_int32();
let mut files: Vec<File> = Vec::new();
for _ in 0..n_files {
message.read_int8();
let name = message.read_string();
let size = message.read_int32();
message.read_int32();
message.read_string();
let n_attribs = message.read_int32();
let mut attribs: HashMap<i32, i32> = HashMap::new();
for _ in 0..n_attribs {
attribs.insert(message.read_int32(), message.read_int32());
}
files.push(File {
username: username.clone(),
name,
size,
attribs,
});
}
let slots = message.read_int8();
let speed = message.read_int32();
Self {
token,
files,
slots,
speed,
}
}
}
pub struct FileSearchResponse;
impl MessageHandler<PeerOperation> for FileSearchResponse {
fn get_code(&self) -> u8 {
@@ -85,7 +38,7 @@ fn test_new_from_message() {
message.set_pointer(8);
let file_search = FileSearchResult::new_from_message(&mut message);
assert_eq!(file_search.token, "6d2b9434");
assert_eq!(file_search.token, 23);
assert_eq!(file_search.files.len(), 2);
let file = &file_search.files[0];
assert_eq!(
+17
View File
@@ -0,0 +1,17 @@
use crate::{
message::{server::MessageFactory, Message, MessageHandler},
peer::PeerOperation,
};
use std::sync::mpsc::Sender;
pub struct GetShareFileList;
impl MessageHandler<PeerOperation> for GetShareFileList {
fn get_code(&self) -> u8 {
4
}
fn handle(&self, _message: &mut Message, sender: Sender<PeerOperation>) {
let message = MessageFactory::build_shared_folders_message(100, 800);
sender.send(PeerOperation::SendMessage(message)).unwrap();
}
}
+3
View File
@@ -1,3 +1,6 @@
pub mod file_search_response;
pub mod get_share_file_list;
pub mod transfer_request;
pub mod upload_failed;
pub use self::file_search_response::*;
+20
View File
@@ -0,0 +1,20 @@
use crate::{
message::{Message, MessageHandler},
peer::PeerOperation,
types::Transfer,
};
use std::sync::mpsc::Sender;
pub struct TransferRequest;
impl MessageHandler<PeerOperation> for TransferRequest {
fn get_code(&self) -> u8 {
40
}
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
let transfer = Transfer::new_from_message(message);
sender
.send(PeerOperation::TransferRequest(transfer))
.unwrap();
}
}
+17
View File
@@ -0,0 +1,17 @@
use crate::{
message::{Message, MessageHandler},
peer::PeerOperation,
types::UploadFailed,
};
use std::sync::mpsc::Sender;
pub struct UploadFailedHandler;
impl MessageHandler<PeerOperation> for UploadFailedHandler {
fn get_code(&self) -> u8 {
46
}
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
let upload_failed = UploadFailed::new_from_message(message);
info!("Upload failed for ${}", upload_failed.filename);
}
}
+7 -1
View File
@@ -1,5 +1,5 @@
use crate::message::{Message, MessageHandler};
use crate::peer::Peer;
use crate::peer::{ConnectionType, Peer};
use crate::server::ServerOperation;
use std::sync::mpsc::Sender;
pub struct ConnectToPeerHandler;
@@ -9,6 +9,12 @@ impl MessageHandler<ServerOperation> for ConnectToPeerHandler {
}
fn handle(&self, message: &mut Message, sender: Sender<ServerOperation>) {
let peer = Peer::new_from_message(message);
match peer.connection_type {
ConnectionType::P => (),
ConnectionType::F => trace!("[server] ConnectToPeer {:?}", peer),
ConnectionType::D => (),
}
sender.send(ServerOperation::ConnectToPeer(peer)).unwrap();
}
}
@@ -20,6 +20,6 @@ impl MessageHandler<ServerOperation> for ExcludedSearchPhrasesHandler {
let phrase = message.read_string();
exluded_phrases.push(phrase);
}
println!("Excluded search phrases: {:?}", exluded_phrases);
debug!("Excluded search phrases: {:?}", exluded_phrases);
}
}
+2 -2
View File
@@ -12,11 +12,11 @@ impl MessageHandler<ServerOperation> for FileSearchHandler {
26
}
fn handle(&self, message: &mut Message, _sender: Sender<ServerOperation>) {
println!("Handling file search message");
debug!("Handling file search message");
let username = message.read_string();
let token = message.read_int32();
let query = message.read_string();
println!(
info!(
"Message search username:{}, token: {}, query: {}",
username, token, query
);
+2 -2
View File
@@ -20,9 +20,9 @@ impl MessageHandler<ServerOperation> for LoginHandler {
return sender.send(ServerOperation::LoginStatus(false)).unwrap();
}
println!("Login successful");
info!("Login successful");
let greeting = message.read_string();
println!("Server greeting: {:?}", greeting);
debug!("Server greeting: {:?}", greeting);
sender.send(ServerOperation::LoginStatus(true)).unwrap();
}
+29 -8
View File
@@ -1,4 +1,4 @@
use crate::{message::Message, utils::md5};
use crate::{message::Message, types::Transfer, utils::md5};
pub struct MessageFactory;
impl MessageFactory {
@@ -18,7 +18,7 @@ impl MessageFactory {
// ]
// .to_vec(),
// )
// .clone()
// .clone()fac
let hash = md5([username, password].join("").as_str());
let mut message = Message::new();
@@ -34,14 +34,14 @@ impl MessageFactory {
message
}
pub fn build_shared_folders_message(folder_count: i32, file_count: i32) -> Message {
pub fn build_shared_folders_message(folder_count: u32, file_count: u32) -> Message {
Message::new()
.write_int32(35)
.write_int32(folder_count)
.write_int32(file_count)
.clone()
}
pub fn build_file_search_message(token: i32, query: &str) -> Message {
pub fn build_file_search_message(token: u32, query: &str) -> Message {
// Message::new_with_data(
// [
// 26, 0, 0, 0, 219, 178, 47, 28, 11, 0, 0, 0, 116, 104, 101, 32, 119, 101, 101, 107, 101,
@@ -56,7 +56,7 @@ impl MessageFactory {
.write_string(query)
.clone()
}
pub fn build_set_status_message(status_code: i32) -> Message {
pub fn build_set_status_message(status_code: u32) -> Message {
Message::new()
.write_int32(28)
.write_int32(status_code)
@@ -68,17 +68,38 @@ impl MessageFactory {
pub fn build_set_wait_port_message() -> Message {
Message::new().write_int32(2).write_int32(2234).clone()
}
pub fn build_watch_user(token: &str) -> Message {
pub fn build_watch_user(token: u32) -> Message {
Message::new()
.write_raw_bytes([5, 0, 0, 0, 0].to_vec())
.write_raw_hex_string(token)
.write_int32(token)
.clone()
}
pub fn build_queue_upload_message(filename: &str) -> Message {
Message::new()
.write_int32(43)
.write_string(filename)
.clone()
}
pub fn build_transfer_response_message(transfer: Transfer) -> Message {
Message::new()
.write_int32(41)
.write_int32(transfer.token)
.write_bool(true)
.clone()
}
pub fn build_pierce_firewall_message(token: u32) -> Message {
Message::new()
.write_int32(0) // PierceFirewall message code
.write_int32(token)
.clone()
}
}
#[test]
fn test_build_watch_user() {
let token = "5b581500";
let token: u32 = 223;
let message = MessageFactory::build_watch_user(token);
let expect: Vec<u8> = [5, 0, 0, 0, 0, 91, 88, 21, 0].to_vec();
+1 -1
View File
@@ -25,7 +25,7 @@ impl MessageHandler<ServerOperation> for MessageUser {
new_message,
);
println!("User message received:",);
info!("User message received:");
user_message.print()
}
}
+1 -1
View File
@@ -15,6 +15,6 @@ impl MessageHandler<ServerOperation> for ParentMinSpeedHandler {
fn handle(&self, message: &mut Message, sender: Sender<ServerOperation>) {
let _ = sender;
let number = message.read_int32();
println!("Parent min speed: {}", number);
debug!("Parent min speed: {}", number);
}
}
+1 -1
View File
@@ -15,6 +15,6 @@ impl MessageHandler<ServerOperation> for ParentSpeedRatioHandler {
fn handle(&self, message: &mut Message, _sender: Sender<ServerOperation>) {
let number = message.read_int32();
println!("Parent min speed: {}", number);
debug!("Parent speed ratio: {}", number);
}
}
+1 -1
View File
@@ -14,6 +14,6 @@ impl MessageHandler<ServerOperation> for PrivilegedUsersHandler {
fn handle(&self, message: &mut Message, _sender: Sender<ServerOperation>) {
let number = message.read_int32();
println!("Number of privilaged users: {}", number);
debug!("Number of privileged users: {}", number);
}
}
+1 -1
View File
@@ -16,6 +16,6 @@ impl MessageHandler<ServerOperation> for WishListIntervalHandler {
fn handle(&self, message: &mut Message, _sender: Sender<ServerOperation>) {
let number = message.read_int32();
println!("Wishlist search interval: {} in seconds", number);
debug!("Wishlist search interval: {} in seconds", number);
}
}
+96 -37
View File
@@ -1,8 +1,11 @@
use crate::dispatcher::MessageDispatcher;
use crate::message::peer::get_share_file_list::GetShareFileList;
use crate::message::peer::transfer_request::TransferRequest;
use crate::message::peer::upload_failed::UploadFailedHandler;
use crate::message::peer::FileSearchResponse;
use crate::message::server::MessageFactory;
use crate::message::{Handlers, Message, MessageReader};
use crate::types::FileSearchResult;
use crate::message::{Handlers, Message, MessageReader, MessageType};
use crate::types::{FileSearchResult, Transfer};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
@@ -20,7 +23,6 @@ pub struct DefaultPeer {
peer: Peer,
peer_channel: Option<Sender<PeerOperation>>,
client_channel: Sender<ClientOperation>,
// Store thread handles for proper lifecycle management
read_thread: Option<JoinHandle<()>>,
write_thread: Option<JoinHandle<()>>,
}
@@ -29,6 +31,7 @@ pub struct DefaultPeer {
pub enum PeerOperation {
SendMessage(Message),
FileSearchResult(FileSearchResult),
TransferRequest(Transfer),
}
impl DefaultPeer {
pub fn new(peer: Peer, client_channel: Sender<ClientOperation>) -> Self {
@@ -46,13 +49,14 @@ impl DefaultPeer {
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid address"))?;
let mut stream = TcpStream::connect_timeout(&socket_address, Duration::from_secs(10))?;
let mut stream = TcpStream::connect_timeout(&socket_address, Duration::from_secs(20))?;
stream.set_read_timeout(Some(Duration::from_secs(5)))?;
stream.set_write_timeout(Some(Duration::from_secs(5)))?;
if let Some(token) = self.peer.token.clone() {
if let Some(token) = self.peer.token {
stream
.write_all(&MessageFactory::build_watch_user(token.as_str()).get_data())
.write_all(&MessageFactory::build_watch_user(token).get_data())
.unwrap();
}
self.start_read_write_loops(stream)?;
@@ -64,6 +68,9 @@ impl DefaultPeer {
let (peer_sender, peer_reader): (Sender<PeerOperation>, Receiver<PeerOperation>) =
mpsc::channel();
// Set the peer_channel so transfer_request can send messages
self.peer_channel = Some(peer_sender.clone());
let mut read_stream = stream.try_clone()?;
let mut write_stream = stream; // Use the original stream for writing
@@ -74,6 +81,9 @@ impl DefaultPeer {
self.read_thread = Some(thread::spawn(move || {
let mut handlers = Handlers::new();
handlers.register_handler(FileSearchResponse);
handlers.register_handler(TransferRequest);
handlers.register_handler(GetShareFileList);
handlers.register_handler(UploadFailedHandler);
let dispatcher = MessageDispatcher::new(peer_sender, handlers);
@@ -83,27 +93,41 @@ impl DefaultPeer {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
println!(
debug!(
"Read operation timed out in default peer {:}:{:}",
peer.host, peer.port
);
continue;
}
Err(e) => {
eprintln!("Error reading from peer: {}. Terminating read loop.", e);
let _ = client_channel_for_read.send(ClientOperation::PeerDisconnected(peer.username.clone()));
error!("Error reading from peer: {}. Terminating read loop.", e);
let _ = client_channel_for_read
.send(ClientOperation::PeerDisconnected(peer.username.clone()));
break;
}
}
match buffered_reader.extract_message() {
Ok(Some(mut message)) => dispatcher.dispatch(&mut message),
Ok(Some(mut message)) => {
trace!(
"[default_peer:{:?}] ← {:?}",
peer.username,
message
.get_message_name(
MessageType::Peer,
message.get_message_code() as u32
)
.map_err(|e| e.to_string())
);
dispatcher.dispatch(&mut message)
}
Err(e) => {
println!(
warn!(
"Error extracting message in default peer: {}. Terminating read loop.",
e
);
let _ = client_channel_for_read.send(ClientOperation::PeerDisconnected(peer.username.clone()));
let _ = client_channel_for_read
.send(ClientOperation::PeerDisconnected(peer.username.clone()));
break;
}
Ok(None) => continue,
@@ -112,29 +136,62 @@ impl DefaultPeer {
}));
let client_channel = self.client_channel.clone();
let peer_channel = self.peer_channel.clone();
let peer_username = self.peer.username.clone();
self.write_thread = Some(thread::spawn(move || {
loop {
match peer_reader.recv() {
Ok(operation) => {
match operation {
PeerOperation::SendMessage(message) => {
if let Err(e) = write_stream.write_all(&message.get_buffer()) {
eprintln!("Error writing message to stream: {}. Terminating write loop.", e);
let _ = client_channel.send(ClientOperation::PeerDisconnected(peer_username.clone()));
break;
}
Ok(operation) => match operation {
PeerOperation::SendMessage(message) => {
trace!(
"[default_peer:{:?}] ➡ {:?} - {:?}",
peer_username,
message
.get_message_name(
MessageType::Peer,
u32::from_le_bytes(
message.get_slice(0, 4).try_into().unwrap()
)
)
.map_err(|e| e.to_string()),
message
);
if let Err(e) = write_stream.write_all(&message.get_buffer()) {
error!("Error writing message to stream: {} - {}. Terminating write loop.", peer_username, e);
let _ = client_channel
.send(ClientOperation::PeerDisconnected(peer_username.clone()));
break;
}
PeerOperation::FileSearchResult(file_search) => {
client_channel
.send(ClientOperation::SearchResult(file_search))
}
PeerOperation::FileSearchResult(file_search) => {
client_channel
.send(ClientOperation::SearchResult(file_search))
.unwrap();
}
PeerOperation::TransferRequest(transfer) => {
debug!(
"[default_peer:{}] transfer request: {:?}",
peer_username, transfer
);
client_channel
.send(ClientOperation::TransferRequest(transfer.clone()))
.unwrap();
let transfer_response =
MessageFactory::build_transfer_response_message(transfer.clone());
if let Some(sender) = peer_channel.clone() {
sender
.send(PeerOperation::SendMessage(transfer_response))
.unwrap();
}
}
}
},
Err(_) => {
// The sender has been dropped, the peer is shutting down.
println!("Peer channel closed. Terminating write loop.");
debug!("Peer channel closed. Terminating write loop.");
break;
}
}
@@ -143,31 +200,33 @@ impl DefaultPeer {
Ok(())
}
pub fn transfer_request(&self, filename: String) -> Result<(), io::Error> {
let message = MessageFactory::build_queue_upload_message(&filename);
if let Some(sender) = &self.peer_channel {
sender.send(PeerOperation::SendMessage(message)).unwrap();
}
Ok(())
}
}
impl Drop for DefaultPeer {
fn drop(&mut self) {
// Signal threads to shut down (e.g., by dropping the sender half of the channel)
// For now, we'll just drop the peer_channel, which will cause the writer thread
// to exit its loop. A more robust solution might involve a shared AtomicBool
// or a dedicated shutdown channel.
self.peer_channel = None;
// Join the read thread
if let Some(handle) = self.read_thread.take() {
println!("Joining read thread...");
debug!("Joining read thread...");
match handle.join() {
Ok(_) => println!("Read thread joined successfully."),
Err(e) => eprintln!("Read thread panicked: {:?}", e),
Ok(_) => debug!("Read thread joined successfully."),
Err(e) => error!("Read thread panicked: {:?}", e),
}
}
// Join the write thread
if let Some(handle) = self.write_thread.take() {
println!("Joining write thread...");
debug!("Joining write thread...");
match handle.join() {
Ok(_) => println!("Write thread joined successfully."),
Err(e) => eprintln!("Write thread panicked: {:?}", e),
Ok(_) => debug!("Write thread joined successfully."),
Err(e) => error!("Write thread panicked: {:?}", e),
}
}
}
+9 -9
View File
@@ -8,7 +8,7 @@ pub struct Listen {}
impl Listen {
pub fn start(port: u32, _server_channel: Sender<ServerOperation>) {
println!("starting listener on port {port}");
info!("starting listener on port {port}");
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).unwrap();
for stream in listener.incoming() {
let mut read_stream = stream.unwrap();
@@ -20,19 +20,19 @@ impl Listen {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
println!("Read operation timed out");
debug!("Read operation timed out");
continue;
}
Err(e) => {
eprintln!("Error reading from server: {}", e);
error!("Error reading from server: {}", e);
break;
}
}
match buffered_reader.extract_message() {
Ok(Some(mut message)) => {
println!("Received message: {:?}", message.get_message_code());
println!("{:?}", message.get_data());
debug!("Received message: {:?}", message.get_message_code());
trace!("{:?}", message.get_data());
message.print_hex();
if message.get_message_code() == 1 {
@@ -45,8 +45,8 @@ impl Listen {
// // message.read_string().parse().unwrap();
// // let token = message.read_int32();
//
println!("type: {:?}", typex);
println!("user: {:?}", size);
debug!("type: {:?}", typex);
debug!("user: {:?}", size);
// server_channel
// .send(ServerOperation::ConnectToPeer(peer))
@@ -54,13 +54,13 @@ impl Listen {
}
}
Err(e) => {
println!("Error extracting message: {}", e)
warn!("Error extracting message: {}", e)
}
Ok(None) => continue,
}
}
println!("Connection established!");
info!("Connection established!");
}
}
}
+12 -13
View File
@@ -46,11 +46,11 @@ pub struct Peer {
pub username: String,
pub connection_type: ConnectionType,
pub host: String,
pub port: i32,
pub token: Option<String>,
pub privileged: i8,
pub unknown: i8,
pub obfuscated_port: i8,
pub port: u32,
pub token: Option<u32>,
pub privileged: u8,
pub unknown: u8,
pub obfuscated_port: u8,
}
impl Peer {
#[allow(clippy::too_many_arguments, dead_code)]
@@ -58,11 +58,11 @@ impl Peer {
username: String,
connection_type: ConnectionType,
host: String,
port: i32,
token: String,
privileged: i8,
unknown: i8,
obfuscated_port: i8,
port: u32,
token: u32,
privileged: u8,
unknown: u8,
obfuscated_port: u8,
) -> Self {
Self {
username,
@@ -78,7 +78,7 @@ impl Peer {
#[allow(dead_code)]
pub fn new_from_message(message: &mut Message) -> Self {
let username = message.read_string();
let connection_type: ConnectionType = message.read_string().parse().unwrap();
let connection_type = message.read_string().parse().unwrap();
let mut ip: Vec<i32> = vec![];
for _ in 0..4 {
@@ -94,7 +94,7 @@ impl Peer {
let (port, token, privileged, unknown, obfuscated_port) = (
message.read_int32(),
message.read_raw_hex_str(4),
message.read_int32(),
message.read_int8(),
message.read_int8(),
message.read_int8(),
@@ -155,4 +155,3 @@ fn test_new_from_message2() {
assert_eq!(peer.unknown, 1);
assert_eq!(peer.obfuscated_port, 0);
}
View File
+82 -30
View File
@@ -15,6 +15,7 @@ use crate::message::Handlers;
use crate::message::Message;
use crate::message::MessageReader;
use crate::peer::listen::Listen;
use crate::peer::ConnectionType;
use crate::peer::Peer;
use std::io::{self, Write};
@@ -71,16 +72,16 @@ impl Context {
}
#[derive(Debug, Clone)]
pub struct UserMessage {
id: i32,
timestamp: i32,
id: u32,
timestamp: u32,
username: String,
message: String,
new_message: bool,
}
impl UserMessage {
pub fn new(
id: i32,
timestamp: i32,
id: u32,
timestamp: u32,
username: String,
message: String,
new_message: bool,
@@ -94,7 +95,7 @@ impl UserMessage {
}
}
pub fn print(&self) {
println!(
debug!(
"Timestamp: {}. User: {}, Id: #{}, New message: {} Message: {}",
self.timestamp, self.username, self.id, self.new_message, self.message
);
@@ -121,19 +122,19 @@ impl Rooms {
#[allow(dead_code)]
pub fn print(&self) {
println!("Public rooms ({}):", self.public_rooms.len());
info!("Public rooms ({}):", self.public_rooms.len());
for room in &self.public_rooms {
room.print();
}
println!("Owned private rooms ({}):", self.owned_private_rooms.len());
info!("Owned private rooms ({}):", self.owned_private_rooms.len());
for room in &self.owned_private_rooms {
room.print();
}
println!("Private rooms ({}):", self.private_rooms.len());
info!("Private rooms ({}):", self.private_rooms.len());
for room in &self.private_rooms {
room.print();
}
println!(
info!(
"Operated private rooms ({}):",
self.operated_private_rooms.len()
);
@@ -160,7 +161,7 @@ impl Room {
self.number_of_users = number_of_users;
}
pub fn print(&self) {
println!(
debug!(
"Room: {}, Number of users: {}",
self.name, self.number_of_users
);
@@ -172,6 +173,7 @@ pub enum ServerOperation {
SendMessage(Message),
#[allow(dead_code)]
ConnectToPeer(Peer),
PierceFirewall(u32),
}
#[derive(Debug)]
@@ -205,6 +207,10 @@ impl Server {
&self.address
}
pub fn get_sender(&self) -> &Sender<ServerOperation> {
&self.sender
}
/// Start reading and writing loops in separate threads
fn start_read_write_loops(
&mut self,
@@ -216,7 +222,7 @@ impl Server {
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid address"))?;
println!(
info!(
"Connecting to server at {}:{}",
self.address.host, self.address.port
);
@@ -261,24 +267,31 @@ impl Server {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
println!("Read operation timed out");
debug!("Read operation timed out");
continue;
}
Err(e) => {
eprintln!("Error reading from server: {}", e);
error!("Error reading from server: {}", e);
break;
}
}
match buffered_reader.extract_message() {
Ok(Some(mut message)) => {
// println!("Received message: {:?}", message.get_message_code_u32());
// message.print_hex();
// trace!(
// "[server] ← {:?}",
// message
// .get_message_name(
// MessageType::Server,
// message.get_message_code() as u32
// )
// .map_err(|e| e.to_string())
// );
dispatcher.dispatch(&mut message)
}
Err(e) => {
println!("Error extracting message: {}", e)
warn!("Error extracting message: {}", e)
}
Ok(None) => continue,
}
@@ -295,24 +308,56 @@ impl Server {
loop {
if let Ok(operation) = server_channel.recv() {
match operation {
ServerOperation::ConnectToPeer(peer) => {
match client_channel.send(ClientOperation::ConnectToPeer(peer)) {
Ok(_) => {}
Err(e) => {
println!("Error sending message: {}", e);
ServerOperation::ConnectToPeer(peer) => match peer.connection_type {
ConnectionType::P => {
match client_channel.send(ClientOperation::ConnectToPeer(peer)) {
Ok(_) => {}
Err(_e) => {}
}
}
}
ConnectionType::F => {
match client_channel.send(ClientOperation::PierceFireWall(peer)) {
Ok(_) => {
debug!("Sent PierceFireWall operation for F-type connection");
}
Err(_e) => {
error!("Failed to send PierceFireWall operation");
}
}
}
ConnectionType::D => {}
},
ServerOperation::LoginStatus(message) => {
context.lock().unwrap().logged_in = Some(message);
}
ServerOperation::PierceFirewall(token) => {
let pierce_message = MessageFactory::build_pierce_firewall_message(token);
match write_stream.write_all(&pierce_message.get_buffer()) {
Ok(_) => {
debug!("Sent PierceFirewall message with token: {}", token);
}
Err(e) => {
error!("Error writing PierceFirewall message: {}", e);
break;
}
}
}
ServerOperation::SendMessage(message) => {
// message.decode();
// message.print_hex2();
// trace!(
// "[server] ➡ {:?}",
// message
// .get_message_name(
// MessageType::Server,
// u32::from_le_bytes(
// message.get_slice(0, 4).try_into().unwrap()
// )
// )
// .map_err(|e| e.to_string()),
// );
match write_stream.write_all(&message.get_buffer()) {
Ok(_) => {}
Err(e) => {
eprintln!("Error writing message to stream : {}", e);
error!("Error writing message to stream : {}", e);
break;
}
}
@@ -328,7 +373,7 @@ impl Server {
fn queue_message(&self, message: Message) {
match self.sender.send(ServerOperation::SendMessage(message)) {
Ok(_) => {}
Err(e) => println!("Failed to send: {}", e),
Err(e) => error!("Failed to send: {}", e),
}
}
fn start_listener(&self, server_channel: Sender<ServerOperation>) {
@@ -347,7 +392,7 @@ impl Server {
// wait till server says your logged in or not
loop {
if start.elapsed() > timeout {
println!("Timeout waiting for login response");
warn!("Timeout waiting for login response");
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Timeout waiting for login response",
@@ -364,9 +409,9 @@ impl Server {
}
if logged_in.unwrap() {
println!("Logged in as {}", username);
info!("Logged in as {}", username);
self.queue_message(MessageFactory::build_set_wait_port_message());
self.queue_message(MessageFactory::build_shared_folders_message(1, 1));
self.queue_message(MessageFactory::build_shared_folders_message(1, 499));
self.queue_message(MessageFactory::build_no_parent_message());
self.queue_message(MessageFactory::build_set_status_message(2));
}
@@ -374,7 +419,14 @@ impl Server {
Ok(logged_in.unwrap())
}
pub fn file_search(&self, token: i32, query: &str) {
pub fn file_search(&self, token: u32, query: &str) {
self.queue_message(MessageFactory::build_file_search_message(token, query));
}
pub fn pierce_firewall(&self, token: u32) {
match self.sender.send(ServerOperation::PierceFirewall(token)) {
Ok(_) => {}
Err(e) => error!("Failed to send PierceFirewall: {}", e),
}
}
}
+98 -5
View File
@@ -1,20 +1,113 @@
use std::collections::HashMap;
use crate::{message::Message, utils::zlib::deflate};
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct File {
pub username: String,
pub name: String,
pub size: i32,
pub attribs: HashMap<i32, i32>,
pub size: u32,
pub attribs: HashMap<u32, u32>,
}
pub struct UploadFailed {
pub filename: String,
}
impl UploadFailed {
pub fn new_from_message(message: &mut Message) -> Self {
let filename = message.read_string();
Self { filename }
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct FileSearchResult {
pub token: String,
pub token: u32,
pub files: Vec<File>,
pub slots: i8,
pub speed: i32,
pub slots: u8,
pub speed: u32,
}
impl FileSearchResult {
pub fn new_from_message(message: &mut Message) -> Self {
let pointer = message.get_pointer();
let size = message.get_size();
let data: Vec<u8> = message.get_slice(pointer, size);
let deflated = deflate(&data).unwrap();
let mut message = Message::new_with_data(deflated);
let username = message.read_string();
let token = message.read_int32();
let n_files = message.read_int32();
let mut files: Vec<File> = Vec::new();
for _ in 0..n_files {
message.read_int8();
let name = message.read_string();
let size = message.read_int32();
message.read_int32();
message.read_string();
let n_attribs = message.read_int32();
let mut attribs: HashMap<u32, u32> = HashMap::new();
for _ in 0..n_attribs {
attribs.insert(message.read_int32(), message.read_int32());
}
files.push(File {
username: username.clone(),
name,
size,
attribs,
});
}
let slots = message.read_int8();
let speed = message.read_int32();
Self {
token,
files,
slots,
speed,
}
}
}
#[derive(Debug, Clone)]
pub struct Transfer {
pub direction: u32,
pub token: u32,
pub filename: String,
pub size: u64,
}
#[derive(Debug, Clone)]
pub struct DownloadResult {
pub filename: String,
pub username: String,
pub status: DownloadStatus,
pub elapsed_time: std::time::Duration,
}
#[derive(Debug, Clone)]
pub enum DownloadStatus {
Pending,
InProgress,
Completed,
Failed,
TimedOut,
}
impl Transfer {
pub fn new_from_message(message: &mut Message) -> Self {
let direction = message.read_int32();
let token = message.read_int32();
let filename = message.read_string();
let size = message.read_int64();
Self {
direction,
token,
filename,
size,
}
}
}
+133
View File
@@ -0,0 +1,133 @@
use std::env;
use std::sync::Once;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
Error = 0,
Warn = 1,
Info = 2,
Debug = 3,
Trace = 4,
}
static INIT: Once = Once::new();
static mut LOG_LEVEL: LogLevel = LogLevel::Debug; // Default to VERBOSE (mapped to DEBUG)
pub fn init() {
INIT.call_once(|| {
let level = env::var("LOG_LEVEL")
.or_else(|_| env::var("RUST_LOG"))
.unwrap_or_else(|_| "VERBOSE".to_string())
.to_uppercase();
unsafe {
LOG_LEVEL = match level.as_str() {
"ERROR" => LogLevel::Error,
"WARN" => LogLevel::Warn,
"INFO" => LogLevel::Info,
"DEBUG" => LogLevel::Debug,
"TRACE" => LogLevel::Trace,
"VERBOSE" => LogLevel::Debug, // Map VERBOSE to DEBUG
_ => LogLevel::Debug, // Default to VERBOSE
};
}
});
}
pub fn log(level: LogLevel, message: &str) {
unsafe {
if level <= LOG_LEVEL {
let level_str = match level {
LogLevel::Error => "\x1b[31mERROR\x1b[0m", // Red
LogLevel::Warn => "\x1b[33mWARN\x1b[0m", // Yellow
LogLevel::Info => "\x1b[32mINFO\x1b[0m", // Green
LogLevel::Debug => "\x1b[34mDEBUG\x1b[0m", // Blue
LogLevel::Trace => "\x1b[35mTRACE\x1b[0m", // Magenta
};
let now = std::time::SystemTime::now();
let datetime = now.duration_since(std::time::UNIX_EPOCH).unwrap();
let secs = datetime.as_secs();
let subsec_millis = datetime.subsec_millis();
// Format as YYYY-MM-DD HH:MM:SS.mmm
let days_since_epoch = secs / 86400;
let days_since_1970 = days_since_epoch as i32;
// Calculate year (approximately)
let mut year = 1970;
let mut remaining_days = days_since_1970;
while remaining_days >= 365 {
let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
let days_in_year = if is_leap { 366 } else { 365 };
if remaining_days >= days_in_year {
remaining_days -= days_in_year;
year += 1;
} else {
break;
}
}
// Calculate month and day (simplified)
let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
let mut month = 1;
let mut day = remaining_days + 1;
for &days_in_month in &month_days {
let actual_days = if month == 2 && is_leap { 29 } else { days_in_month };
if day > actual_days {
day -= actual_days;
month += 1;
} else {
break;
}
}
// Calculate time of day
let seconds_in_day = secs % 86400;
let hours = seconds_in_day / 3600;
let minutes = (seconds_in_day % 3600) / 60;
let seconds = seconds_in_day % 60;
eprintln!("[{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}] [{}] {}",
year, month, day, hours, minutes, seconds, subsec_millis, level_str, message);
}
}
}
#[macro_export]
macro_rules! error {
($($arg:tt)*) => {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Error, &format!($($arg)*))
};
}
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Warn, &format!($($arg)*))
};
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Info, &format!($($arg)*))
};
}
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Debug, &format!($($arg)*))
};
}
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {
$crate::utils::logger::log($crate::utils::logger::LogLevel::Trace, &format!($($arg)*))
};
}
+2
View File
@@ -1,3 +1,5 @@
#[macro_use]
pub mod logger;
pub mod md5;
pub mod thread_pool;
pub mod zlib;