feat: rename module to soulseek-rs
This commit is contained in:
Generated
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "soulseek_download"
|
name = "soulseek-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "soulseek_download"
|
name = "soulseek-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
+319
-72
@@ -1,37 +1,50 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
peer::{ConnectionType, DefaultPeer, Peer},
|
error::{Result, SoulseekRs},
|
||||||
|
peer::{
|
||||||
|
listen::Listen, ConnectionType, DefaultPeer, DownloadPeer, NewPeer,
|
||||||
|
Peer,
|
||||||
|
},
|
||||||
server::{PeerAddress, Server, ServerOperation},
|
server::{PeerAddress, Server, ServerOperation},
|
||||||
types::{FileSearchResult, Transfer},
|
types::{Download, FileSearchResult},
|
||||||
utils::{md5, thread_pool::ThreadPool},
|
utils::{md5, thread_pool::ThreadPool},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
net::TcpStream,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{Receiver, Sender},
|
mpsc::{Receiver, Sender},
|
||||||
Mutex,
|
Mutex,
|
||||||
},
|
},
|
||||||
thread,
|
thread::{self, sleep},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::{mpsc, Arc},
|
sync::{mpsc, Arc},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{debug, error, info, trace, warn};
|
use crate::{debug, error, info, trace};
|
||||||
const MAX_THREADS: usize = 100;
|
const MAX_THREADS: usize = 50;
|
||||||
pub enum ClientOperation {
|
pub enum ClientOperation {
|
||||||
|
NewPeer(NewPeer),
|
||||||
ConnectToPeer(Peer),
|
ConnectToPeer(Peer),
|
||||||
SearchResult(FileSearchResult),
|
SearchResult(FileSearchResult),
|
||||||
PeerDisconnected(String),
|
PeerDisconnected(String),
|
||||||
TransferRequest(Transfer),
|
|
||||||
PierceFireWall(Peer),
|
PierceFireWall(Peer),
|
||||||
|
DownloadFromPeer(u32, Peer),
|
||||||
|
GetPeerAddressResponse {
|
||||||
|
username: String,
|
||||||
|
host: String,
|
||||||
|
port: u32,
|
||||||
|
obfuscation_type: u32,
|
||||||
|
obfuscated_port: u16,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
struct ClientContext {
|
struct ClientContext {
|
||||||
peers: HashMap<String, DefaultPeer>,
|
peers: HashMap<String, DefaultPeer>,
|
||||||
sender: Option<Sender<ClientOperation>>,
|
sender: Option<Sender<ClientOperation>>,
|
||||||
server_sender: Option<Sender<crate::server::ServerOperation>>,
|
server_sender: Option<Sender<crate::server::ServerOperation>>,
|
||||||
search_results: Vec<FileSearchResult>,
|
search_results: Vec<FileSearchResult>,
|
||||||
downloads: HashMap<u32, Transfer>,
|
downloads: HashMap<u32, Download>,
|
||||||
thread_pool: ThreadPool,
|
thread_pool: ThreadPool,
|
||||||
}
|
}
|
||||||
impl ClientContext {
|
impl ClientContext {
|
||||||
@@ -61,12 +74,17 @@ impl Client {
|
|||||||
password: String,
|
password: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
crate::utils::logger::init();
|
crate::utils::logger::init();
|
||||||
|
let context = Arc::new(Mutex::new(ClientContext::new()));
|
||||||
|
debug!(
|
||||||
|
"ThreadPool initialized with {} threads",
|
||||||
|
context.lock().unwrap().thread_pool.thread_count()
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
address,
|
address,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
server: None,
|
server: None,
|
||||||
context: Arc::new(Mutex::new(ClientContext::new())),
|
context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +96,8 @@ impl Client {
|
|||||||
|
|
||||||
self.context.lock().unwrap().sender = Some(sender.clone());
|
self.context.lock().unwrap().sender = Some(sender.clone());
|
||||||
|
|
||||||
// self.read_form_channel(message_reader);
|
let client_sender = sender.clone();
|
||||||
|
|
||||||
self.server = match Server::new(self.address.clone(), sender) {
|
self.server = match Server::new(self.address.clone(), sender) {
|
||||||
Ok(server) => {
|
Ok(server) => {
|
||||||
info!(
|
info!(
|
||||||
@@ -87,13 +106,17 @@ impl Client {
|
|||||||
server.get_address().get_port()
|
server.get_address().get_port()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Store the server sender in the client context
|
// thread::spawn(move || {
|
||||||
self.context.lock().unwrap().server_sender =
|
// Listen::start(2234, client_sender.clone());
|
||||||
|
// });
|
||||||
|
let mut unlocked_context = self.context.lock().unwrap();
|
||||||
|
unlocked_context.server_sender =
|
||||||
Some(server.get_sender().clone());
|
Some(server.get_sender().clone());
|
||||||
|
|
||||||
Self::listen_to_client_operations(
|
Self::listen_to_client_operations(
|
||||||
message_reader,
|
message_reader,
|
||||||
self.context.clone(),
|
self.context.clone(),
|
||||||
|
self.username.clone(),
|
||||||
);
|
);
|
||||||
Some(server)
|
Some(server)
|
||||||
}
|
}
|
||||||
@@ -104,24 +127,18 @@ impl Client {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(&self) -> Result<bool, std::io::Error> {
|
pub fn login(&self) -> Result<bool> {
|
||||||
// Attempt to login
|
// Attempt to login
|
||||||
info!("Logging in as {}", self.username);
|
info!("Logging in as {}", self.username);
|
||||||
if let Some(server) = &self.server {
|
if let Some(server) = &self.server {
|
||||||
let result = server.login(&self.username, &self.password);
|
let result = server.login(&self.username, &self.password)?;
|
||||||
if result.unwrap() {
|
if result {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(
|
Err(SoulseekRs::AuthenticationFailed)
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
"Error logging in",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(
|
Err(SoulseekRs::NotConnected)
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
"Not connected to server",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,78 +154,85 @@ impl Client {
|
|||||||
&self,
|
&self,
|
||||||
query: &str,
|
query: &str,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
) -> Vec<FileSearchResult> {
|
) -> Result<Vec<FileSearchResult>> {
|
||||||
info!("Searching for {}", query);
|
info!("Searching for {}", query);
|
||||||
if let Some(server) = &self.server {
|
if let Some(server) = &self.server {
|
||||||
let hash = md5::md5(query);
|
let hash = md5::md5(query);
|
||||||
let token = u32::from_str_radix(&hash[0..5], 16).unwrap();
|
let token = u32::from_str_radix(&hash[0..5], 16)?;
|
||||||
server.file_search(token, query);
|
server.file_search(token, query);
|
||||||
} else {
|
} else {
|
||||||
warn!("Not connected to server");
|
return Err(SoulseekRs::NotConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
loop {
|
loop {
|
||||||
|
sleep(Duration::from_millis(500));
|
||||||
if start.elapsed() >= timeout {
|
if start.elapsed() >= timeout {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.context.lock().unwrap().search_results.clone();
|
Ok(self.context.lock().unwrap().search_results.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn download(
|
pub fn download(
|
||||||
&self,
|
&self,
|
||||||
filename: String,
|
filename: String,
|
||||||
username: String,
|
username: String,
|
||||||
) -> crate::types::DownloadResult {
|
size: u64,
|
||||||
|
) -> Result<crate::types::DownloadResult> {
|
||||||
use crate::types::{DownloadResult, DownloadStatus};
|
use crate::types::{DownloadResult, DownloadStatus};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
println!("Downloading {} from {}", filename, username);
|
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
// Start the download request
|
let hash = md5::md5(&filename);
|
||||||
let context = self.context.lock().unwrap();
|
let token = u32::from_str_radix(&hash[0..5], 16)?;
|
||||||
|
|
||||||
|
let download = Download {
|
||||||
|
username: username.clone(),
|
||||||
|
filename: filename.clone(),
|
||||||
|
token,
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut context = self.context.lock().unwrap();
|
||||||
|
context.downloads.insert(token, download.clone());
|
||||||
let download_initiated = context
|
let download_initiated = context
|
||||||
.peers
|
.peers
|
||||||
.get(&username)
|
.get(&username)
|
||||||
.map(|p| p.transfer_request(filename.clone()))
|
.map(|p| p.transfer_request(download.clone()))
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
drop(context);
|
drop(context);
|
||||||
|
|
||||||
let timeout = Duration::from_secs(50);
|
let timeout = Duration::from_secs(150);
|
||||||
let check_interval = Duration::from_millis(100);
|
let check_interval = Duration::from_millis(100);
|
||||||
|
|
||||||
if !download_initiated {
|
if !download_initiated {
|
||||||
return DownloadResult {
|
return Ok(DownloadResult {
|
||||||
filename,
|
filename,
|
||||||
username,
|
username,
|
||||||
status: DownloadStatus::Failed,
|
status: DownloadStatus::Failed,
|
||||||
elapsed_time: start_time.elapsed(),
|
elapsed_time: start_time.elapsed(),
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-blocking wait loop
|
|
||||||
while start_time.elapsed() < timeout {
|
while start_time.elapsed() < timeout {
|
||||||
// Check download status (for now just wait, actual download logic will be implemented later)
|
|
||||||
std::thread::sleep(check_interval);
|
std::thread::sleep(check_interval);
|
||||||
|
|
||||||
// TODO: Check actual download progress here
|
|
||||||
// For now, we'll just wait for the timeout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadResult {
|
Ok(DownloadResult {
|
||||||
filename,
|
filename,
|
||||||
username,
|
username,
|
||||||
status: DownloadStatus::TimedOut,
|
status: DownloadStatus::TimedOut,
|
||||||
elapsed_time: start_time.elapsed(),
|
elapsed_time: start_time.elapsed(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listen_to_client_operations(
|
fn listen_to_client_operations(
|
||||||
reader: Receiver<ClientOperation>,
|
reader: Receiver<ClientOperation>,
|
||||||
client_context: Arc<Mutex<ClientContext>>,
|
client_context: Arc<Mutex<ClientContext>>,
|
||||||
|
own_username: String,
|
||||||
) {
|
) {
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
if let Ok(operation) = reader.recv() {
|
if let Ok(operation) = reader.recv() {
|
||||||
@@ -219,10 +243,17 @@ impl Client {
|
|||||||
ConnectionType::F => {
|
ConnectionType::F => {
|
||||||
debug!("Peer with F {:?}", peer)
|
debug!("Peer with F {:?}", peer)
|
||||||
}
|
}
|
||||||
ConnectionType::D => (),
|
ConnectionType::D => {
|
||||||
|
error!("ConnectionType::D not implemented for peer: {}", peer.username);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::connect_to_peer(peer, client_context.clone())
|
Self::connect_to_peer(
|
||||||
|
peer,
|
||||||
|
client_context.clone(),
|
||||||
|
own_username.clone(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ClientOperation::SearchResult(file_search) => {
|
ClientOperation::SearchResult(file_search) => {
|
||||||
client_context
|
client_context
|
||||||
@@ -237,55 +268,272 @@ impl Client {
|
|||||||
drop(peer); // Explicitly drop to trigger cleanup
|
drop(peer); // Explicitly drop to trigger cleanup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientOperation::TransferRequest(transfer) => {
|
ClientOperation::PierceFireWall(peer) => {
|
||||||
client_context
|
Self::pierce_firewall(
|
||||||
|
peer,
|
||||||
|
client_context.clone(),
|
||||||
|
own_username.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ClientOperation::DownloadFromPeer(token, peer) => {
|
||||||
|
// {
|
||||||
|
// let mut context = client_context.lock().unwrap();
|
||||||
|
// if let Some(default_peer) =
|
||||||
|
// context.peers.remove(&peer.username)
|
||||||
|
// {
|
||||||
|
// default_peer.disconnect();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
let maybe_download = {
|
||||||
|
let client_context = client_context.lock().unwrap();
|
||||||
|
client_context.downloads.get(&token).cloned()
|
||||||
|
};
|
||||||
|
let own_username = own_username.clone();
|
||||||
|
|
||||||
|
match maybe_download {
|
||||||
|
Some(download) => {
|
||||||
|
thread::spawn(move || {
|
||||||
|
let download_peer = DownloadPeer::new(
|
||||||
|
download.username.clone(),
|
||||||
|
peer.host.clone(),
|
||||||
|
peer.port,
|
||||||
|
download.token,
|
||||||
|
false,
|
||||||
|
own_username,
|
||||||
|
);
|
||||||
|
let filename: Option<&str> =
|
||||||
|
download.filename.split('\\').last();
|
||||||
|
match filename {
|
||||||
|
Some(filename) => {
|
||||||
|
match download_peer.download_file(
|
||||||
|
Some(download.size as usize),
|
||||||
|
Some(format!("/tmp/{}", filename)),
|
||||||
|
) {
|
||||||
|
Ok(bytes) => {
|
||||||
|
info!("Successfully downloaded {} bytes to /tmp/{}", bytes, filename);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"Failed to download file '{}' from {}:{} (token: {}) - Error: {}",
|
||||||
|
filename, peer.host, peer.port, download.token, e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => error!("Cant find filename to save download: {:?}", download.filename),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!(
|
||||||
|
"Can't find download with token {:?}",
|
||||||
|
token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ClientOperation::NewPeer(new_peer) => {
|
||||||
|
if client_context
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.downloads
|
.peers
|
||||||
.insert(transfer.token, transfer);
|
.contains_key(&new_peer.username)
|
||||||
|
{
|
||||||
|
debug!(
|
||||||
|
"Already connected to {}",
|
||||||
|
new_peer.username
|
||||||
|
);
|
||||||
|
} else if let Some(server_sender) =
|
||||||
|
&client_context.lock().unwrap().server_sender
|
||||||
|
{
|
||||||
|
server_sender
|
||||||
|
.send(ServerOperation::GetPeerAddress(
|
||||||
|
new_peer.username.clone(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = new_peer.tcp_stream.peer_addr().unwrap();
|
||||||
|
let host = addr.ip().to_string();
|
||||||
|
let port: u32 = addr.port().into();
|
||||||
|
|
||||||
|
let peer = Peer {
|
||||||
|
username: new_peer.username.clone(),
|
||||||
|
connection_type: new_peer.connection_type,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
token: Some(new_peer.token),
|
||||||
|
privileged: None,
|
||||||
|
obfuscated_port: None,
|
||||||
|
unknown: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::connect_to_peer(
|
||||||
|
peer,
|
||||||
|
client_context.clone(),
|
||||||
|
own_username.clone(),
|
||||||
|
Some(new_peer.tcp_stream),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ClientOperation::PierceFireWall(peer) => {
|
ClientOperation::GetPeerAddressResponse {
|
||||||
Self::pierce_firewall(peer, client_context.clone());
|
username,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
obfuscation_type,
|
||||||
|
obfuscated_port,
|
||||||
|
} => {
|
||||||
|
debug!(
|
||||||
|
"Received peer address for {}: {}:{} (obf_type: {}, obf_port: {})",
|
||||||
|
username, host, port, obfuscation_type, obfuscated_port
|
||||||
|
);
|
||||||
|
|
||||||
|
match client_context
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.peers
|
||||||
|
.get(&username)
|
||||||
|
{
|
||||||
|
Some(_peer) => {
|
||||||
|
// don't know if i should update? and or reconnect the peer
|
||||||
|
// debug!(
|
||||||
|
// "existing peer: {:?}, new peer details:
|
||||||
|
// username: {},
|
||||||
|
// host: {},
|
||||||
|
// port: {}
|
||||||
|
// obfuscation_type: {}
|
||||||
|
// obfuscated_port: {}",
|
||||||
|
// peer,
|
||||||
|
// username,
|
||||||
|
// host,
|
||||||
|
// port,
|
||||||
|
// obfuscation_type,
|
||||||
|
// obfuscated_port,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let peer = Peer::new(
|
||||||
|
username,
|
||||||
|
ConnectionType::P,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
obfuscation_type.try_into().unwrap(),
|
||||||
|
obfuscated_port.try_into().unwrap(),
|
||||||
|
);
|
||||||
|
Self::connect_to_peer(
|
||||||
|
peer,
|
||||||
|
client_context.clone(),
|
||||||
|
own_username.clone(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_to_peer(peer: Peer, client_context: Arc<Mutex<ClientContext>>) {
|
fn connect_to_peer(
|
||||||
let context = client_context.clone();
|
peer: Peer,
|
||||||
let unlocked_context = context.lock().unwrap();
|
client_context: Arc<Mutex<ClientContext>>,
|
||||||
|
own_username: String,
|
||||||
|
stream: Option<TcpStream>,
|
||||||
|
) {
|
||||||
|
let client_context2 = client_context.clone();
|
||||||
|
let unlocked_context = client_context.lock().unwrap();
|
||||||
|
|
||||||
if let Some(sender) = &unlocked_context.sender {
|
if let Some(sender) = unlocked_context.sender.clone() {
|
||||||
if !unlocked_context.peers.contains_key(&peer.username) {
|
if !unlocked_context.peers.contains_key(&peer.username) {
|
||||||
let peer_clone = peer.clone();
|
let peer_clone = peer.clone();
|
||||||
let sender_clone = sender.clone();
|
let sender_clone = sender;
|
||||||
unlocked_context.thread_pool.execute(move || {
|
unlocked_context.thread_pool.execute(move || {
|
||||||
let default_peer =
|
trace!("[client] connecting to {}, with connection_type: {}", peer.username, peer.connection_type);
|
||||||
DefaultPeer::new(peer_clone, sender_clone);
|
match peer.connection_type {
|
||||||
match default_peer.connect() {
|
ConnectionType::P => {
|
||||||
Ok(p) => {
|
let default_peer =
|
||||||
let mut context = client_context.lock().unwrap();
|
DefaultPeer::new(peer_clone, sender_clone);
|
||||||
context.peers.insert(peer.username, p);
|
|
||||||
|
let connect_result = match stream {
|
||||||
|
Some(s) => default_peer.connect_with_socket(s),
|
||||||
|
None => default_peer.connect()
|
||||||
|
};
|
||||||
|
|
||||||
|
match connect_result {
|
||||||
|
Ok(p) => {
|
||||||
|
trace!("[client] connected to: {}", peer.username);
|
||||||
|
client_context2.lock().unwrap().peers.insert(peer.username, p);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
trace!(
|
||||||
|
"Can't connect to {:?} {:?}:{:?} {:?} - {:?}",
|
||||||
|
peer.username,
|
||||||
|
peer.host,
|
||||||
|
peer.port,
|
||||||
|
peer.connection_type,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
|
||||||
trace!(
|
ConnectionType::F => {
|
||||||
"Can't connect to {} {}:{} {:?} - {}",
|
let context = client_context2.lock().unwrap();
|
||||||
peer.username,
|
let download =
|
||||||
peer.host,
|
context.downloads.get(&peer.token.unwrap());
|
||||||
peer.port,
|
|
||||||
peer.connection_type,
|
match download {
|
||||||
e
|
Some(download) => {
|
||||||
);
|
let download_peer = DownloadPeer::new(
|
||||||
|
peer.username,
|
||||||
|
peer.host,
|
||||||
|
peer.port,
|
||||||
|
peer.token.unwrap(),
|
||||||
|
true,
|
||||||
|
own_username.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let filename: Option<&str> =
|
||||||
|
download.filename.split('\\').last();
|
||||||
|
match filename {
|
||||||
|
Some(filename) => {
|
||||||
|
download_peer
|
||||||
|
.download_file(
|
||||||
|
Some(
|
||||||
|
download.size as usize,
|
||||||
|
),
|
||||||
|
Some(format!(
|
||||||
|
"/tmp/{}",
|
||||||
|
filename
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
None => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConnectionType::D => {
|
||||||
|
error!("ConnectionType::D not implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// debug!("[client] Peer already connected: {}", peer.username);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("No sender found");
|
error!("No sender found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn pierce_firewall(peer: Peer, client_context: Arc<Mutex<ClientContext>>) {
|
fn pierce_firewall(
|
||||||
|
peer: Peer,
|
||||||
|
client_context: Arc<Mutex<ClientContext>>,
|
||||||
|
own_username: String,
|
||||||
|
) {
|
||||||
debug!("Piercing firewall for peer: {:?}", peer);
|
debug!("Piercing firewall for peer: {:?}", peer);
|
||||||
|
|
||||||
let context = client_context.lock().unwrap();
|
let context = client_context.lock().unwrap();
|
||||||
@@ -309,7 +557,6 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drop(context);
|
drop(context);
|
||||||
// Also try to connect to the peer directly
|
Self::connect_to_peer(peer, client_context, own_username, None);
|
||||||
Self::connect_to_peer(peer, client_context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -3,15 +3,23 @@ use std::sync::mpsc::Sender;
|
|||||||
|
|
||||||
use crate::warn;
|
use crate::warn;
|
||||||
|
|
||||||
/// Add `<Op>` to make it generic over any operation type.
|
|
||||||
pub struct MessageDispatcher<Op> {
|
pub struct MessageDispatcher<Op> {
|
||||||
|
owner_name: String,
|
||||||
sender: Sender<Op>,
|
sender: Sender<Op>,
|
||||||
handlers: Handlers<Op>,
|
handlers: Handlers<Op>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Op> MessageDispatcher<Op> {
|
impl<Op> MessageDispatcher<Op> {
|
||||||
pub fn new(sender: Sender<Op>, handlers: Handlers<Op>) -> Self {
|
pub fn new(
|
||||||
MessageDispatcher { sender, handlers }
|
owner_name: String,
|
||||||
|
sender: Sender<Op>,
|
||||||
|
handlers: Handlers<Op>,
|
||||||
|
) -> Self {
|
||||||
|
MessageDispatcher {
|
||||||
|
owner_name,
|
||||||
|
sender,
|
||||||
|
handlers,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch(&self, message: &mut Message) {
|
pub fn dispatch(&self, message: &mut Message) {
|
||||||
@@ -22,7 +30,8 @@ impl<Op> MessageDispatcher<Op> {
|
|||||||
handler.handle(message, self.sender.clone());
|
handler.handle(message, self.sender.clone());
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"No handler found for message code: {:?}",
|
"[{}:dispatcher] No handler found for message code: {}",
|
||||||
|
self.owner_name,
|
||||||
message.get_message_code()
|
message.get_message_code()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
|
/// Custom error type for the Soulseek download library
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SoulseekRs {
|
||||||
|
/// Network-related errors (I/O, connection failures)
|
||||||
|
NetworkError(std::io::Error),
|
||||||
|
/// Authentication failed during login
|
||||||
|
AuthenticationFailed,
|
||||||
|
/// Error parsing messages or data
|
||||||
|
ParseError(String),
|
||||||
|
/// Operation timed out
|
||||||
|
Timeout,
|
||||||
|
/// Connection was closed unexpectedly
|
||||||
|
ConnectionClosed,
|
||||||
|
/// Invalid message format or content
|
||||||
|
InvalidMessage(String),
|
||||||
|
/// Server not connected
|
||||||
|
NotConnected,
|
||||||
|
/// Compression/decompression error
|
||||||
|
CompressionError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SoulseekRs {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SoulseekRs::NetworkError(err) => {
|
||||||
|
write!(f, "Network error: {}", err)
|
||||||
|
}
|
||||||
|
SoulseekRs::AuthenticationFailed => {
|
||||||
|
write!(f, "Authentication failed")
|
||||||
|
}
|
||||||
|
SoulseekRs::ParseError(msg) => write!(f, "Parse error: {}", msg),
|
||||||
|
SoulseekRs::Timeout => write!(f, "Operation timed out"),
|
||||||
|
SoulseekRs::ConnectionClosed => write!(f, "Connection closed"),
|
||||||
|
SoulseekRs::InvalidMessage(msg) => {
|
||||||
|
write!(f, "Invalid message: {}", msg)
|
||||||
|
}
|
||||||
|
SoulseekRs::NotConnected => write!(f, "Not connected to server"),
|
||||||
|
SoulseekRs::CompressionError(msg) => {
|
||||||
|
write!(f, "Compression error: {}", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for SoulseekRs {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
SoulseekRs::NetworkError(err) => Some(err),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for SoulseekRs {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
SoulseekRs::NetworkError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::num::ParseIntError> for SoulseekRs {
|
||||||
|
fn from(err: std::num::ParseIntError) -> Self {
|
||||||
|
SoulseekRs::ParseError(format!("Integer parse error: {}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SoulseekRs {
|
||||||
|
fn from(err: String) -> Self {
|
||||||
|
SoulseekRs::CompressionError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result type alias for the Soulseek library
|
||||||
|
pub type Result<T> = std::result::Result<T, SoulseekRs>;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Core modules
|
// Core modules
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod dispatcher;
|
pub mod dispatcher;
|
||||||
|
pub mod error;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod peer;
|
pub mod peer;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
@@ -18,6 +19,7 @@ pub mod prelude {
|
|||||||
|
|
||||||
// Re-export commonly used types
|
// Re-export commonly used types
|
||||||
pub use client::Client;
|
pub use client::Client;
|
||||||
|
pub use error::{Result, SoulseekRs};
|
||||||
pub use server::PeerAddress;
|
pub use server::PeerAddress;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
DownloadResult, DownloadStatus, File, FileSearchResult, Transfer,
|
DownloadResult, DownloadStatus, File, FileSearchResult, Transfer,
|
||||||
|
|||||||
+26
-10
@@ -1,24 +1,40 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
use soulseek_download::{Client, PeerAddress};
|
use soulseek_rs::{Client, PeerAddress};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
let mut client = Client::new(
|
let mut client = Client::new(
|
||||||
PeerAddress::new(String::from("server.slsknet.org"), 2242),
|
// PeerAddress::new(String::from("server.slsknet.org"), 2242),
|
||||||
|
PeerAddress::new(String::from("127.0.0.1"), 2242),
|
||||||
String::from("insane_in_the_brain3"),
|
String::from("insane_in_the_brain3"),
|
||||||
String::from("13375137"),
|
String::from("13375137"),
|
||||||
);
|
);
|
||||||
|
|
||||||
client.connect();
|
client.connect();
|
||||||
match client.login() {
|
match client.login() {
|
||||||
Ok(_) => {
|
Ok(_) => match client
|
||||||
let results = client.search("Fantazia", Duration::from_secs(10));
|
.search("michel test file", Duration::from_secs(20))
|
||||||
println!("Search results: {} - {:?} ", results.len(), results);
|
{
|
||||||
if !results.is_empty() && !results[0].files.is_empty() {
|
Ok(results) => {
|
||||||
let file = results[0].files[0].clone();
|
if let Some(result) =
|
||||||
let download_result = client.download(file.name, file.username);
|
results.iter().find(|r| !r.files.is_empty())
|
||||||
println!("Download result: {:?}", download_result);
|
{
|
||||||
|
let file = result.files[0].clone();
|
||||||
|
match client.download(file.name, file.username, file.size) {
|
||||||
|
Ok(download_result) => {
|
||||||
|
println!("Download result: {:?}", download_result);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to download: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprint!("No results")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(e) => {
|
||||||
|
eprintln!("Failed to search: {}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to login: {}", e);
|
eprintln!("Failed to login: {}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-2
@@ -5,11 +5,10 @@ mod message_reader;
|
|||||||
pub mod peer;
|
pub mod peer;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
// Re-export commonly used items
|
|
||||||
pub use handlers::{Handlers, MessageHandler};
|
pub use handlers::{Handlers, MessageHandler};
|
||||||
pub use message_reader::MessageReader;
|
pub use message_reader::MessageReader;
|
||||||
|
|
||||||
use std::str;
|
use std::{str, usize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -148,6 +147,10 @@ impl Message {
|
|||||||
self.data[4]
|
self.data[4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_message_code_send(&self) -> u8 {
|
||||||
|
self.data[0]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_with_data(data: Vec<u8>) -> Self {
|
pub fn new_with_data(data: Vec<u8>) -> Self {
|
||||||
Self { data, pointer: 0 }
|
Self { data, pointer: 0 }
|
||||||
}
|
}
|
||||||
@@ -222,6 +225,21 @@ impl Message {
|
|||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_raw_byte(&mut self) -> Vec<u8> {
|
||||||
|
if self.pointer + 4 > self.data.len() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = vec![
|
||||||
|
self.data[self.pointer],
|
||||||
|
self.data[self.pointer + 1],
|
||||||
|
self.data[self.pointer + 2],
|
||||||
|
self.data[self.pointer + 3],
|
||||||
|
];
|
||||||
|
self.pointer += 4;
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_int32(&mut self) -> u32 {
|
pub fn read_int32(&mut self) -> u32 {
|
||||||
if self.pointer + 4 > self.data.len() {
|
if self.pointer + 4 > self.data.len() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -250,6 +268,11 @@ impl Message {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_int8(&mut self, value: u8) -> &mut Self {
|
||||||
|
self.data.extend_from_slice(&value.to_le_bytes());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_int32(&mut self, value: u32) -> &mut Self {
|
pub fn write_int32(&mut self, value: u32) -> &mut Self {
|
||||||
self.data.extend_from_slice(&value.to_le_bytes());
|
self.data.extend_from_slice(&value.to_le_bytes());
|
||||||
self
|
self
|
||||||
@@ -311,9 +334,16 @@ impl Message {
|
|||||||
41 => Ok("Relogged"),
|
41 => Ok("Relogged"),
|
||||||
42 => Ok("UserSearch"),
|
42 => Ok("UserSearch"),
|
||||||
64 => Ok("RoomList"),
|
64 => Ok("RoomList"),
|
||||||
|
69 => Ok("PrivilegedUsers"),
|
||||||
|
71 => Ok("HaveNoParent"),
|
||||||
|
83 => Ok("ParentMinSpeed"),
|
||||||
|
84 => Ok("ParentSpeedRatio"),
|
||||||
92 => Ok("CheckPrivileges"),
|
92 => Ok("CheckPrivileges"),
|
||||||
|
93 => Ok("EmbeddedMessage"),
|
||||||
100 => Ok("AcceptChildren"),
|
100 => Ok("AcceptChildren"),
|
||||||
102 => Ok("PossibleParents"),
|
102 => Ok("PossibleParents"),
|
||||||
|
104 => Ok("WishlistInterval"),
|
||||||
|
160 => Ok("ExcludedSearchPhrases"),
|
||||||
1001 => Ok("CantConnectToPeer"),
|
1001 => Ok("CantConnectToPeer"),
|
||||||
_ => {
|
_ => {
|
||||||
Err(Error(format!("Unknown server message code: {}", code)))
|
Err(Error(format!("Unknown server message code: {}", code)))
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ impl MessageHandler<PeerOperation> for FileSearchResponse {
|
|||||||
9
|
9
|
||||||
}
|
}
|
||||||
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
||||||
let file_search = FileSearchResult::new_from_message(message);
|
let file_search = match FileSearchResult::new_from_message(message) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(_) => return, // Skip malformed search results
|
||||||
|
};
|
||||||
|
|
||||||
sender
|
sender
|
||||||
.send(PeerOperation::FileSearchResult(file_search))
|
.send(PeerOperation::FileSearchResult(file_search))
|
||||||
@@ -40,7 +43,7 @@ fn test_new_from_message() {
|
|||||||
let mut message = Message::new_with_data(data);
|
let mut message = Message::new_with_data(data);
|
||||||
message.set_pointer(8);
|
message.set_pointer(8);
|
||||||
|
|
||||||
let file_search = FileSearchResult::new_from_message(&mut message);
|
let file_search = FileSearchResult::new_from_message(&mut message).unwrap();
|
||||||
assert_eq!(file_search.token, 882125677);
|
assert_eq!(file_search.token, 882125677);
|
||||||
assert_eq!(file_search.files.len(), 2);
|
assert_eq!(file_search.files.len(), 2);
|
||||||
let file = &file_search.files[0];
|
let file = &file_search.files[0];
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
mod file_search_response;
|
mod file_search_response;
|
||||||
mod get_share_file_list;
|
mod get_share_file_list;
|
||||||
|
mod place_in_queue_response;
|
||||||
mod transfer_request;
|
mod transfer_request;
|
||||||
|
mod transfer_response;
|
||||||
mod upload_failed;
|
mod upload_failed;
|
||||||
|
|
||||||
// Re-export handlers
|
// Re-export handlers
|
||||||
pub use file_search_response::FileSearchResponse;
|
pub use file_search_response::FileSearchResponse;
|
||||||
pub use get_share_file_list::GetShareFileList;
|
pub use get_share_file_list::GetShareFileList;
|
||||||
|
pub use place_in_queue_response::PlaceInQueueResponse;
|
||||||
pub use transfer_request::TransferRequest;
|
pub use transfer_request::TransferRequest;
|
||||||
|
pub use transfer_response::TransferResponse;
|
||||||
pub use upload_failed::UploadFailedHandler;
|
pub use upload_failed::UploadFailedHandler;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use crate::{
|
||||||
|
message::{Message, MessageHandler},
|
||||||
|
peer::PeerOperation,
|
||||||
|
};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
pub struct PlaceInQueueResponse;
|
||||||
|
|
||||||
|
impl MessageHandler<PeerOperation> for PlaceInQueueResponse {
|
||||||
|
fn get_code(&self) -> u8 {
|
||||||
|
43
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
||||||
|
let filename = message.read_string();
|
||||||
|
let place = message.read_int32() as u32;
|
||||||
|
|
||||||
|
sender
|
||||||
|
.send(PeerOperation::PlaceInQueueResponse { filename, place })
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
message::{Message, MessageHandler},
|
message::{Message, MessageHandler},
|
||||||
peer::PeerOperation,
|
peer::PeerOperation,
|
||||||
|
trace,
|
||||||
types::Transfer,
|
types::Transfer,
|
||||||
};
|
};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
@@ -11,6 +12,7 @@ impl MessageHandler<PeerOperation> for TransferRequest {
|
|||||||
40
|
40
|
||||||
}
|
}
|
||||||
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
||||||
|
trace!("transfer_response: {:?}", message.get_buffer());
|
||||||
let transfer = Transfer::new_from_message(message);
|
let transfer = Transfer::new_from_message(message);
|
||||||
|
|
||||||
sender
|
sender
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::{
|
||||||
|
message::{Message, MessageHandler},
|
||||||
|
peer::PeerOperation,
|
||||||
|
};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
pub struct TransferResponse;
|
||||||
|
|
||||||
|
impl MessageHandler<PeerOperation> for TransferResponse {
|
||||||
|
fn get_code(&self) -> u8 {
|
||||||
|
41
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(&self, message: &mut Message, sender: Sender<PeerOperation>) {
|
||||||
|
let token = message.read_int32();
|
||||||
|
let allowed = message.read_int8();
|
||||||
|
let reason = (allowed == 0).then(|| message.read_string());
|
||||||
|
|
||||||
|
sender
|
||||||
|
.send(PeerOperation::TransferResponse {
|
||||||
|
token,
|
||||||
|
allowed: allowed == 1,
|
||||||
|
reason,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::message::{Message, MessageHandler};
|
use crate::message::{Message, MessageHandler};
|
||||||
use crate::peer::{ConnectionType, Peer};
|
use crate::peer::Peer;
|
||||||
use crate::server::ServerOperation;
|
use crate::server::ServerOperation;
|
||||||
use crate::trace;
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
pub struct ConnectToPeerHandler;
|
pub struct ConnectToPeerHandler;
|
||||||
impl MessageHandler<ServerOperation> for ConnectToPeerHandler {
|
impl MessageHandler<ServerOperation> for ConnectToPeerHandler {
|
||||||
@@ -10,12 +9,8 @@ impl MessageHandler<ServerOperation> for ConnectToPeerHandler {
|
|||||||
}
|
}
|
||||||
fn handle(&self, message: &mut Message, sender: Sender<ServerOperation>) {
|
fn handle(&self, message: &mut Message, sender: Sender<ServerOperation>) {
|
||||||
let peer = Peer::new_from_message(message);
|
let peer = Peer::new_from_message(message);
|
||||||
match peer.connection_type {
|
println!("ConnectToPeerHandler: {:?}", peer);
|
||||||
ConnectionType::P => (),
|
println!("ConnectToPeerHandler: {:?}", peer.username);
|
||||||
ConnectionType::F => trace!("[server] ConnectToPeer {:?}", peer),
|
|
||||||
ConnectionType::D => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.send(ServerOperation::ConnectToPeer(peer)).unwrap();
|
sender.send(ServerOperation::ConnectToPeer(peer)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use crate::message::{Message, MessageHandler};
|
||||||
|
use crate::server::ServerOperation;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
pub struct GetPeerAddressHandler;
|
||||||
|
|
||||||
|
impl MessageHandler<ServerOperation> for GetPeerAddressHandler {
|
||||||
|
fn get_code(&self) -> u8 {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(&self, message: &mut Message, sender: Sender<ServerOperation>) {
|
||||||
|
let username = message.read_string();
|
||||||
|
|
||||||
|
// Read IP address as 4 bytes
|
||||||
|
let mut ip: Vec<u8> = vec![];
|
||||||
|
for _ in 0..4 {
|
||||||
|
ip.push(message.read_int8());
|
||||||
|
}
|
||||||
|
let host = format!("{}.{}.{}.{}", ip[3], ip[2], ip[1], ip[0]);
|
||||||
|
|
||||||
|
let port = message.read_int32();
|
||||||
|
let obfuscation_type = message.read_int32();
|
||||||
|
let obfuscated_port = message.read_int32() as u16;
|
||||||
|
println!("GetPeerAddressHandler: {:?}", username); // Debug print
|
||||||
|
|
||||||
|
sender
|
||||||
|
.send(ServerOperation::GetPeerAddressResponse {
|
||||||
|
username,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
obfuscation_type,
|
||||||
|
obfuscated_port,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,13 @@ use crate::{
|
|||||||
|
|
||||||
pub struct MessageFactory;
|
pub struct MessageFactory;
|
||||||
impl MessageFactory {
|
impl MessageFactory {
|
||||||
|
pub fn build_get_peer_address(username: &str) -> Message {
|
||||||
|
let mut message = Message::new();
|
||||||
|
|
||||||
|
message.write_int32(3);
|
||||||
|
message.write_string(username);
|
||||||
|
message
|
||||||
|
}
|
||||||
pub fn build_login_message(username: &str, password: &str) -> Message {
|
pub fn build_login_message(username: &str, password: &str) -> Message {
|
||||||
// Message::new_with_data(
|
// Message::new_with_data(
|
||||||
// [
|
// [
|
||||||
@@ -31,9 +38,8 @@ impl MessageFactory {
|
|||||||
.write_string(password)
|
.write_string(password)
|
||||||
.write_int32(157) // version
|
.write_int32(157) // version
|
||||||
.write_string(&hash)
|
.write_string(&hash)
|
||||||
.write_int32(100); //minor version
|
.write_int32(100)
|
||||||
|
.clone()
|
||||||
message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_shared_folders_message(
|
pub fn build_shared_folders_message(
|
||||||
@@ -47,14 +53,6 @@ impl MessageFactory {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
pub fn build_file_search_message(token: u32, 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,
|
|
||||||
// 110, 100, 0, 0,
|
|
||||||
// ]
|
|
||||||
// .to_vec(),
|
|
||||||
// )
|
|
||||||
// .clone()
|
|
||||||
Message::new()
|
Message::new()
|
||||||
.write_int32(26)
|
.write_int32(26)
|
||||||
.write_int32(token)
|
.write_int32(token)
|
||||||
@@ -68,10 +66,15 @@ impl MessageFactory {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
pub fn build_no_parent_message() -> Message {
|
pub fn build_no_parent_message() -> Message {
|
||||||
Message::new().write_int32(71).write_int32(1).clone()
|
Message::new().write_int32(71).write_bool(true).clone()
|
||||||
}
|
}
|
||||||
pub fn build_set_wait_port_message() -> Message {
|
pub fn build_set_wait_port_message(port: u32) -> Message {
|
||||||
Message::new().write_int32(2).write_int32(2234).clone()
|
Message::new()
|
||||||
|
.write_int32(2)
|
||||||
|
.write_int32(port)
|
||||||
|
// .write_int32(0)
|
||||||
|
// .write_int32(port) // should be different port
|
||||||
|
.clone()
|
||||||
}
|
}
|
||||||
pub fn build_watch_user(token: u32) -> Message {
|
pub fn build_watch_user(token: u32) -> Message {
|
||||||
Message::new()
|
Message::new()
|
||||||
@@ -87,11 +90,23 @@ impl MessageFactory {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_transfer_request_message(
|
||||||
|
filename: &str,
|
||||||
|
token: u32,
|
||||||
|
) -> Message {
|
||||||
|
Message::new()
|
||||||
|
.write_int32(40) // code
|
||||||
|
.write_int32(0) // direction
|
||||||
|
.write_int32(token)
|
||||||
|
.write_string(filename)
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
pub fn build_transfer_response_message(transfer: Transfer) -> Message {
|
pub fn build_transfer_response_message(transfer: Transfer) -> Message {
|
||||||
Message::new()
|
Message::new()
|
||||||
.write_int32(41)
|
.write_int32(41)
|
||||||
.write_int32(transfer.token)
|
.write_int32(transfer.token)
|
||||||
.write_bool(true)
|
.write_bool(true)
|
||||||
|
.write_int64(transfer.size)
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
pub fn build_pierce_firewall_message(token: u32) -> Message {
|
pub fn build_pierce_firewall_message(token: u32) -> Message {
|
||||||
@@ -108,6 +123,7 @@ impl MessageFactory {
|
|||||||
token: u32,
|
token: u32,
|
||||||
) -> Message {
|
) -> Message {
|
||||||
Message::new()
|
Message::new()
|
||||||
|
.write_int8(1)
|
||||||
.write_string(own_username)
|
.write_string(own_username)
|
||||||
.write_string(&connection_type.to_string())
|
.write_string(&connection_type.to_string())
|
||||||
.write_int32(token)
|
.write_int32(token)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod connect_to_peer;
|
mod connect_to_peer;
|
||||||
mod excluded_search_phrases;
|
mod excluded_search_phrases;
|
||||||
mod file_search;
|
mod file_search;
|
||||||
|
mod get_peer_address;
|
||||||
mod login;
|
mod login;
|
||||||
mod message_factory;
|
mod message_factory;
|
||||||
mod message_user;
|
mod message_user;
|
||||||
@@ -10,10 +11,10 @@ mod privileged_users;
|
|||||||
mod room_list;
|
mod room_list;
|
||||||
mod wish_list_interval;
|
mod wish_list_interval;
|
||||||
|
|
||||||
// Re-export handlers
|
|
||||||
pub use connect_to_peer::ConnectToPeerHandler;
|
pub use connect_to_peer::ConnectToPeerHandler;
|
||||||
pub use excluded_search_phrases::ExcludedSearchPhrasesHandler;
|
pub use excluded_search_phrases::ExcludedSearchPhrasesHandler;
|
||||||
pub use file_search::FileSearchHandler;
|
pub use file_search::FileSearchHandler;
|
||||||
|
pub use get_peer_address::GetPeerAddressHandler;
|
||||||
pub use login::LoginHandler;
|
pub use login::LoginHandler;
|
||||||
pub use message_factory::MessageFactory;
|
pub use message_factory::MessageFactory;
|
||||||
pub use message_user::MessageUser;
|
pub use message_user::MessageUser;
|
||||||
|
|||||||
+171
-73
@@ -1,11 +1,12 @@
|
|||||||
use crate::dispatcher::MessageDispatcher;
|
use crate::dispatcher::MessageDispatcher;
|
||||||
use crate::message::peer::FileSearchResponse;
|
use crate::message::peer::FileSearchResponse;
|
||||||
use crate::message::peer::{
|
use crate::message::peer::{
|
||||||
GetShareFileList, TransferRequest, UploadFailedHandler,
|
GetShareFileList, PlaceInQueueResponse, TransferRequest, TransferResponse,
|
||||||
|
UploadFailedHandler,
|
||||||
};
|
};
|
||||||
use crate::message::server::MessageFactory;
|
use crate::message::server::MessageFactory;
|
||||||
use crate::message::{Handlers, Message, MessageReader, MessageType};
|
use crate::message::{Handlers, Message, MessageReader, MessageType};
|
||||||
use crate::types::{FileSearchResult, Transfer};
|
use crate::types::{Download, FileSearchResult, Transfer};
|
||||||
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
@@ -20,6 +21,7 @@ use std::net::ToSocketAddrs;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DefaultPeer {
|
pub struct DefaultPeer {
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
peer_channel: Option<Sender<PeerOperation>>,
|
peer_channel: Option<Sender<PeerOperation>>,
|
||||||
@@ -33,6 +35,15 @@ pub enum PeerOperation {
|
|||||||
SendMessage(Message),
|
SendMessage(Message),
|
||||||
FileSearchResult(FileSearchResult),
|
FileSearchResult(FileSearchResult),
|
||||||
TransferRequest(Transfer),
|
TransferRequest(Transfer),
|
||||||
|
TransferResponse {
|
||||||
|
token: u32,
|
||||||
|
allowed: bool,
|
||||||
|
reason: Option<String>,
|
||||||
|
},
|
||||||
|
PlaceInQueueResponse {
|
||||||
|
filename: String,
|
||||||
|
place: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
impl DefaultPeer {
|
impl DefaultPeer {
|
||||||
pub fn new(peer: Peer, client_channel: Sender<ClientOperation>) -> Self {
|
pub fn new(peer: Peer, client_channel: Sender<ClientOperation>) -> Self {
|
||||||
@@ -44,7 +55,43 @@ impl DefaultPeer {
|
|||||||
write_thread: None,
|
write_thread: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn disconnect(mut self) {
|
||||||
|
debug!("[default_peer:{}] disconnect", self.peer.username.clone());
|
||||||
|
if let Err(e) = self.client_channel.send(
|
||||||
|
ClientOperation::PeerDisconnected(self.peer.username.clone()),
|
||||||
|
) {
|
||||||
|
error!("Failed to send disconnect notification: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.peer_channel.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_with_socket(
|
||||||
|
mut self,
|
||||||
|
mut stream: TcpStream,
|
||||||
|
) -> Result<Self, io::Error> {
|
||||||
|
if let Some(token) = self.peer.token {
|
||||||
|
let mut message: Vec<u8> = [0, 5, 0, 0, 0, 0].to_vec();
|
||||||
|
message.extend_from_slice(&token.to_le_bytes());
|
||||||
|
stream.write_all(&message).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.set_read_timeout(Some(Duration::from_secs(5)))?;
|
||||||
|
stream.set_write_timeout(Some(Duration::from_secs(5)))?;
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"[default_peer:{}] connect_with_socket: direct",
|
||||||
|
self.peer.username
|
||||||
|
);
|
||||||
|
self.start_read_write_loops(stream)?;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
pub fn connect(mut self) -> Result<Self, io::Error> {
|
pub fn connect(mut self) -> Result<Self, io::Error> {
|
||||||
|
println!(
|
||||||
|
"[default_peer] Connecting to {} on port {}",
|
||||||
|
self.peer.host, self.peer.port
|
||||||
|
);
|
||||||
let socket_address = format!("{}:{}", self.peer.host, self.peer.port)
|
let socket_address = format!("{}:{}", self.peer.host, self.peer.port)
|
||||||
.to_socket_addrs()?
|
.to_socket_addrs()?
|
||||||
.next()
|
.next()
|
||||||
@@ -52,6 +99,7 @@ impl DefaultPeer {
|
|||||||
io::Error::new(io::ErrorKind::InvalidInput, "Invalid address")
|
io::Error::new(io::ErrorKind::InvalidInput, "Invalid address")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
trace!("[default_peer:{}] connect: indirect", self.peer.username);
|
||||||
let mut stream = TcpStream::connect_timeout(
|
let mut stream = TcpStream::connect_timeout(
|
||||||
&socket_address,
|
&socket_address,
|
||||||
Duration::from_secs(20),
|
Duration::from_secs(20),
|
||||||
@@ -74,6 +122,10 @@ impl DefaultPeer {
|
|||||||
&mut self,
|
&mut self,
|
||||||
stream: TcpStream,
|
stream: TcpStream,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{}] start_read_write_loops",
|
||||||
|
self.peer.username
|
||||||
|
);
|
||||||
let (peer_sender, peer_reader): (
|
let (peer_sender, peer_reader): (
|
||||||
Sender<PeerOperation>,
|
Sender<PeerOperation>,
|
||||||
Receiver<PeerOperation>,
|
Receiver<PeerOperation>,
|
||||||
@@ -86,17 +138,23 @@ impl DefaultPeer {
|
|||||||
let mut write_stream = stream; // Use the original stream for writing
|
let mut write_stream = stream; // Use the original stream for writing
|
||||||
|
|
||||||
let peer = self.peer.clone();
|
let peer = self.peer.clone();
|
||||||
|
let peer_clone = self.peer.clone();
|
||||||
let client_channel_for_read = self.client_channel.clone();
|
let client_channel_for_read = self.client_channel.clone();
|
||||||
|
|
||||||
// Spawn the reader thread
|
|
||||||
self.read_thread = Some(thread::spawn(move || {
|
self.read_thread = Some(thread::spawn(move || {
|
||||||
let mut handlers = Handlers::new();
|
let mut handlers = Handlers::new();
|
||||||
handlers.register_handler(FileSearchResponse);
|
handlers.register_handler(FileSearchResponse);
|
||||||
handlers.register_handler(TransferRequest);
|
handlers.register_handler(TransferRequest);
|
||||||
|
handlers.register_handler(TransferResponse);
|
||||||
handlers.register_handler(GetShareFileList);
|
handlers.register_handler(GetShareFileList);
|
||||||
handlers.register_handler(UploadFailedHandler);
|
handlers.register_handler(UploadFailedHandler);
|
||||||
|
handlers.register_handler(PlaceInQueueResponse);
|
||||||
|
|
||||||
let dispatcher = MessageDispatcher::new(peer_sender, handlers);
|
let dispatcher = MessageDispatcher::new(
|
||||||
|
"default_peer".to_string(),
|
||||||
|
peer_sender,
|
||||||
|
handlers,
|
||||||
|
);
|
||||||
|
|
||||||
let mut buffered_reader = MessageReader::new();
|
let mut buffered_reader = MessageReader::new();
|
||||||
loop {
|
loop {
|
||||||
@@ -113,7 +171,7 @@ impl DefaultPeer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error reading from peer: {}. Terminating read loop.", e);
|
error!("[default_peer:{}] Error reading from peer: {}. Terminating read loop.",peer.username, e);
|
||||||
let _ = client_channel_for_read.send(
|
let _ = client_channel_for_read.send(
|
||||||
ClientOperation::PeerDisconnected(
|
ClientOperation::PeerDisconnected(
|
||||||
peer.username.clone(),
|
peer.username.clone(),
|
||||||
@@ -147,6 +205,7 @@ impl DefaultPeer {
|
|||||||
peer.username.clone(),
|
peer.username.clone(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
@@ -158,77 +217,109 @@ impl DefaultPeer {
|
|||||||
let peer_channel = self.peer_channel.clone();
|
let peer_channel = self.peer_channel.clone();
|
||||||
let peer_username = self.peer.username.clone();
|
let peer_username = self.peer.username.clone();
|
||||||
|
|
||||||
self.write_thread = Some(thread::spawn(move || {
|
self.write_thread = Some(thread::spawn(move || loop {
|
||||||
loop {
|
match peer_reader.recv() {
|
||||||
match peer_reader.recv() {
|
Ok(operation) => match operation {
|
||||||
Ok(operation) => {
|
PeerOperation::SendMessage(message) => {
|
||||||
match operation {
|
trace!(
|
||||||
PeerOperation::SendMessage(message) => {
|
"[default_peer:{:?}] ➡ {:?} - {:?}",
|
||||||
trace!(
|
peer_username,
|
||||||
"[default_peer:{:?}] ➡ {:?} - {:?}",
|
message
|
||||||
peer_username,
|
.get_message_name(
|
||||||
message
|
MessageType::Peer,
|
||||||
.get_message_name(
|
u32::from_le_bytes(
|
||||||
MessageType::Peer,
|
message
|
||||||
u32::from_le_bytes(
|
.get_slice(0, 4)
|
||||||
message
|
.try_into()
|
||||||
.get_slice(0, 4)
|
.unwrap()
|
||||||
.try_into()
|
)
|
||||||
.unwrap()
|
)
|
||||||
)
|
.map_err(|e| e.to_string()),
|
||||||
)
|
message
|
||||||
.map_err(|e| e.to_string()),
|
);
|
||||||
message
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = write_stream
|
if let Err(e) =
|
||||||
.write_all(&message.get_buffer())
|
write_stream.write_all(&message.get_buffer())
|
||||||
{
|
{
|
||||||
error!("Error writing message to stream: {} - {}. Terminating write loop.", peer_username, e);
|
error!("Error writing message to stream: {} - {}. Terminating write loop.", peer_username, e);
|
||||||
let _ = client_channel.send(
|
let _ = client_channel.send(
|
||||||
ClientOperation::PeerDisconnected(
|
ClientOperation::PeerDisconnected(
|
||||||
peer_username.clone(),
|
peer_username.clone(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
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(_) => {
|
PeerOperation::FileSearchResult(file_search) => {
|
||||||
// The sender has been dropped, the peer is shutting down.
|
client_channel
|
||||||
debug!("Peer channel closed. Terminating write loop.");
|
.send(ClientOperation::SearchResult(file_search))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
PeerOperation::TransferRequest(transfer) => {
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{:}] TransferRequest for {}",
|
||||||
|
peer_username, transfer.token
|
||||||
|
);
|
||||||
|
|
||||||
|
let transfer_response =
|
||||||
|
MessageFactory::build_transfer_response_message(
|
||||||
|
transfer.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(sender) = peer_channel.clone() {
|
||||||
|
sender
|
||||||
|
.send(PeerOperation::SendMessage(
|
||||||
|
transfer_response,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{:}] TransferResponse for {}",
|
||||||
|
peer_username, transfer.token
|
||||||
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
PeerOperation::TransferResponse {
|
||||||
|
token,
|
||||||
|
allowed,
|
||||||
|
reason,
|
||||||
|
} => {
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{}] transfer response token: {} allowed: {}",
|
||||||
|
peer_username, token, allowed
|
||||||
|
);
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
if let Some(reason_text) = reason {
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{}] Transfer rejected: {} - token {}, I will receive TransferRequest soon...",
|
||||||
|
peer_username.clone(),
|
||||||
|
reason_text,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("[default_peer:{:}] Transfer allowed, ready to connect with token {:}",peer_username, token);
|
||||||
|
client_channel
|
||||||
|
.send(ClientOperation::DownloadFromPeer(
|
||||||
|
token,
|
||||||
|
peer_clone.clone(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PeerOperation::PlaceInQueueResponse { filename, place } => {
|
||||||
|
debug!(
|
||||||
|
"[default_peer:{}] Place in queue response - file: {}, place: {}",
|
||||||
|
peer_username, filename, place
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
debug!("[default_peer:{}] Peer channel closed. Terminating write loop.", peer_username);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -236,8 +327,14 @@ impl DefaultPeer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer_request(&self, filename: String) -> Result<(), io::Error> {
|
pub fn transfer_request(
|
||||||
let message = MessageFactory::build_queue_upload_message(&filename);
|
&self,
|
||||||
|
download: Download,
|
||||||
|
) -> Result<(), io::Error> {
|
||||||
|
let message = MessageFactory::build_transfer_request_message(
|
||||||
|
&download.filename,
|
||||||
|
download.token,
|
||||||
|
);
|
||||||
if let Some(sender) = &self.peer_channel {
|
if let Some(sender) = &self.peer_channel {
|
||||||
sender.send(PeerOperation::SendMessage(message)).unwrap();
|
sender.send(PeerOperation::SendMessage(message)).unwrap();
|
||||||
}
|
}
|
||||||
@@ -248,6 +345,7 @@ impl DefaultPeer {
|
|||||||
impl Drop for DefaultPeer {
|
impl Drop for DefaultPeer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.peer_channel = None;
|
self.peer_channel = None;
|
||||||
|
trace!("[default_peer:{}] drop", self.peer.username);
|
||||||
|
|
||||||
if let Some(handle) = self.read_thread.take() {
|
if let Some(handle) = self.read_thread.take() {
|
||||||
debug!("Joining read thread...");
|
debug!("Joining read thread...");
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
received_messages[0],
|
received_messages[0],
|
||||||
vec![
|
vec![
|
||||||
12, 0, 0, 0, 111, 119, 110, 95, 117, 115, 101, 114, 110, 97,
|
1, 12, 0, 0, 0, 111, 119, 110, 95, 117, 115, 101, 114, 110, 97,
|
||||||
109, 101, 1, 0, 0, 0, 70, 33, 0, 0, 0
|
109, 101, 1, 0, 0, 0, 70, 33, 0, 0, 0
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
+38
-28
@@ -1,30 +1,29 @@
|
|||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::{io, net::TcpListener};
|
use std::{io, net::TcpListener};
|
||||||
|
|
||||||
|
use crate::client::ClientOperation;
|
||||||
use crate::message::MessageReader;
|
use crate::message::MessageReader;
|
||||||
use crate::server::ServerOperation;
|
|
||||||
|
|
||||||
|
use crate::peer::NewPeer;
|
||||||
use crate::{debug, error, info, trace, warn};
|
use crate::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
pub struct Listen {}
|
pub struct Listen {}
|
||||||
|
|
||||||
impl Listen {
|
impl Listen {
|
||||||
pub fn start(port: u32, _server_channel: Sender<ServerOperation>) {
|
pub fn start(port: u32, client_sender: Sender<ClientOperation>) {
|
||||||
info!("starting listener on port {port}");
|
info!("starting listener on port {port}");
|
||||||
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).unwrap();
|
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).unwrap();
|
||||||
for stream in listener.incoming() {
|
for stream in listener.incoming() {
|
||||||
let mut read_stream = stream.unwrap();
|
let mut read_stream = stream.unwrap();
|
||||||
|
let mut stop = false;
|
||||||
|
|
||||||
let mut buffered_reader = MessageReader::new();
|
let mut buffered_reader = MessageReader::new();
|
||||||
loop {
|
while !stop {
|
||||||
match buffered_reader.read_from_socket(&mut read_stream) {
|
match buffered_reader.read_from_socket(&mut read_stream) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
|
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
|
||||||
debug!("Read operation timed out");
|
debug!("Read operation timed out");
|
||||||
continue;
|
continue;
|
||||||
@@ -37,39 +36,50 @@ impl Listen {
|
|||||||
|
|
||||||
match buffered_reader.extract_message() {
|
match buffered_reader.extract_message() {
|
||||||
Ok(Some(mut message)) => {
|
Ok(Some(mut message)) => {
|
||||||
debug!(
|
message.set_pointer(4);
|
||||||
"Received message: {:?}",
|
let message_code = message.read_int8();
|
||||||
message.get_message_code()
|
|
||||||
|
trace!(
|
||||||
|
"[listener] Received message with code: {}",
|
||||||
|
message_code
|
||||||
);
|
);
|
||||||
trace!("{:?}", message.get_data());
|
|
||||||
message.print_hex();
|
|
||||||
|
|
||||||
if message.get_message_code() == 1 {
|
match message_code {
|
||||||
let size = message.read_int32();
|
0 => {
|
||||||
message.set_pointer(8);
|
let token = message.read_string();
|
||||||
let typex = message.read_string();
|
debug!("[listener] received Pierce Firewall, token: {}", token);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let tcp_stream =
|
||||||
|
read_stream.try_clone().unwrap();
|
||||||
|
let new_peer = NewPeer::new_from_message(
|
||||||
|
&mut message,
|
||||||
|
tcp_stream,
|
||||||
|
);
|
||||||
|
|
||||||
// let user = message.read_string();
|
trace!("[listener] Handling peer init message: {:?}",new_peer);
|
||||||
// // let connection_type: ConnectionType =
|
client_sender
|
||||||
// // message.read_string().parse().unwrap();
|
.send(ClientOperation::NewPeer(new_peer))
|
||||||
// // let token = message.read_int32();
|
.unwrap();
|
||||||
//
|
stop = true;
|
||||||
debug!("type: {:?}", typex);
|
debug!("[listener] stop");
|
||||||
debug!("user: {:?}", size);
|
}
|
||||||
|
_ => {
|
||||||
// server_channel
|
debug!(
|
||||||
// .send(ServerOperation::ConnectToPeer(peer))
|
"[listener] unknown message_code: {}",
|
||||||
// .unwrap();
|
message_code
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Error extracting message: {}", e)
|
warn!("[listener] Error extracting message: {}", e)
|
||||||
}
|
}
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Connection established!");
|
// info!("[listener] Connection established!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-21
@@ -2,12 +2,38 @@ mod default_peer;
|
|||||||
mod download_peer;
|
mod download_peer;
|
||||||
pub mod listen;
|
pub mod listen;
|
||||||
|
|
||||||
// Re-export commonly used items
|
|
||||||
pub use default_peer::{DefaultPeer, PeerOperation};
|
pub use default_peer::{DefaultPeer, PeerOperation};
|
||||||
|
pub use download_peer::DownloadPeer;
|
||||||
|
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::str::FromStr;
|
use std::{net::TcpStream, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct NewPeer {
|
||||||
|
pub username: String,
|
||||||
|
pub connection_type: ConnectionType,
|
||||||
|
pub token: u32,
|
||||||
|
pub tcp_stream: TcpStream,
|
||||||
|
}
|
||||||
|
impl NewPeer {
|
||||||
|
pub fn new_from_message(
|
||||||
|
message: &mut Message,
|
||||||
|
tcp_stream: TcpStream,
|
||||||
|
) -> Self {
|
||||||
|
let username = message.read_string();
|
||||||
|
let connection_type = message.read_string().parse().unwrap();
|
||||||
|
let token = message.read_int32();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
username,
|
||||||
|
connection_type,
|
||||||
|
token,
|
||||||
|
tcp_stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ConnectionType {
|
pub enum ConnectionType {
|
||||||
@@ -16,7 +42,6 @@ pub enum ConnectionType {
|
|||||||
D,
|
D,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the error type
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseConnectionTypeError;
|
pub struct ParseConnectionTypeError;
|
||||||
|
|
||||||
@@ -28,7 +53,6 @@ impl fmt::Display for ParseConnectionTypeError {
|
|||||||
|
|
||||||
impl std::error::Error for ParseConnectionTypeError {}
|
impl std::error::Error for ParseConnectionTypeError {}
|
||||||
|
|
||||||
// Implement FromStr for ConnectionType
|
|
||||||
impl FromStr for ConnectionType {
|
impl FromStr for ConnectionType {
|
||||||
type Err = ParseConnectionTypeError;
|
type Err = ParseConnectionTypeError;
|
||||||
|
|
||||||
@@ -61,9 +85,9 @@ pub struct Peer {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u32,
|
pub port: u32,
|
||||||
pub token: Option<u32>,
|
pub token: Option<u32>,
|
||||||
pub privileged: u8,
|
pub privileged: Option<u8>,
|
||||||
pub unknown: u8,
|
pub unknown: Option<u8>,
|
||||||
pub obfuscated_port: u8,
|
pub obfuscated_port: Option<u8>,
|
||||||
}
|
}
|
||||||
impl Peer {
|
impl Peer {
|
||||||
#[allow(clippy::too_many_arguments, dead_code)]
|
#[allow(clippy::too_many_arguments, dead_code)]
|
||||||
@@ -72,7 +96,7 @@ impl Peer {
|
|||||||
connection_type: ConnectionType,
|
connection_type: ConnectionType,
|
||||||
host: String,
|
host: String,
|
||||||
port: u32,
|
port: u32,
|
||||||
token: u32,
|
token: Option<u32>,
|
||||||
privileged: u8,
|
privileged: u8,
|
||||||
unknown: u8,
|
unknown: u8,
|
||||||
obfuscated_port: u8,
|
obfuscated_port: u8,
|
||||||
@@ -82,10 +106,10 @@ impl Peer {
|
|||||||
connection_type,
|
connection_type,
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
token: Some(token),
|
token,
|
||||||
privileged,
|
privileged: Some(privileged),
|
||||||
unknown,
|
unknown: Some(unknown),
|
||||||
obfuscated_port,
|
obfuscated_port: Some(obfuscated_port),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -119,9 +143,9 @@ impl Peer {
|
|||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
token: Some(token),
|
token: Some(token),
|
||||||
privileged,
|
privileged: Some(privileged),
|
||||||
unknown,
|
unknown: Some(unknown),
|
||||||
obfuscated_port,
|
obfuscated_port: Some(obfuscated_port),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,9 +166,9 @@ fn test_new_from_message() {
|
|||||||
assert_eq!(peer.host, "45.37.231.27");
|
assert_eq!(peer.host, "45.37.231.27");
|
||||||
assert_eq!(peer.port, 2234);
|
assert_eq!(peer.port, 2234);
|
||||||
assert_eq!(peer.token, Some(1658546));
|
assert_eq!(peer.token, Some(1658546));
|
||||||
assert_eq!(peer.privileged, 0);
|
assert_eq!(peer.privileged, Some(0));
|
||||||
assert_eq!(peer.unknown, 0);
|
assert_eq!(peer.unknown, Some(0));
|
||||||
assert_eq!(peer.obfuscated_port, 0);
|
assert_eq!(peer.obfuscated_port, Some(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -158,6 +182,8 @@ fn test_new_from_message2() {
|
|||||||
let mut message = Message::new_with_data(data);
|
let mut message = Message::new_with_data(data);
|
||||||
message.set_pointer(8);
|
message.set_pointer(8);
|
||||||
|
|
||||||
|
println!("code: {}", message.get_message_code_u32());
|
||||||
|
|
||||||
let peer = Peer::new_from_message(&mut message);
|
let peer = Peer::new_from_message(&mut message);
|
||||||
|
|
||||||
assert_eq!(peer.username, "grandpag");
|
assert_eq!(peer.username, "grandpag");
|
||||||
@@ -165,7 +191,7 @@ fn test_new_from_message2() {
|
|||||||
assert_eq!(peer.host, "68.193.128.137");
|
assert_eq!(peer.host, "68.193.128.137");
|
||||||
assert_eq!(peer.port, 2235);
|
assert_eq!(peer.port, 2235);
|
||||||
assert_eq!(peer.token, Some(4154));
|
assert_eq!(peer.token, Some(4154));
|
||||||
assert_eq!(peer.privileged, 0);
|
assert_eq!(peer.privileged, Some(0));
|
||||||
assert_eq!(peer.unknown, 1);
|
assert_eq!(peer.unknown, Some(1));
|
||||||
assert_eq!(peer.obfuscated_port, 0);
|
assert_eq!(peer.obfuscated_port, Some(0));
|
||||||
}
|
}
|
||||||
|
|||||||
+107
-93
@@ -1,8 +1,10 @@
|
|||||||
use crate::client::ClientOperation;
|
use crate::client::ClientOperation;
|
||||||
use crate::dispatcher::MessageDispatcher;
|
use crate::dispatcher::MessageDispatcher;
|
||||||
|
use crate::error::{Result, SoulseekRs};
|
||||||
use crate::message::server::ConnectToPeerHandler;
|
use crate::message::server::ConnectToPeerHandler;
|
||||||
use crate::message::server::ExcludedSearchPhrasesHandler;
|
use crate::message::server::ExcludedSearchPhrasesHandler;
|
||||||
use crate::message::server::FileSearchHandler;
|
use crate::message::server::FileSearchHandler;
|
||||||
|
use crate::message::server::GetPeerAddressHandler;
|
||||||
use crate::message::server::LoginHandler;
|
use crate::message::server::LoginHandler;
|
||||||
use crate::message::server::MessageFactory;
|
use crate::message::server::MessageFactory;
|
||||||
use crate::message::server::MessageUser;
|
use crate::message::server::MessageUser;
|
||||||
@@ -11,10 +13,9 @@ use crate::message::server::ParentSpeedRatioHandler;
|
|||||||
use crate::message::server::PrivilegedUsersHandler;
|
use crate::message::server::PrivilegedUsersHandler;
|
||||||
use crate::message::server::RoomListHandler;
|
use crate::message::server::RoomListHandler;
|
||||||
use crate::message::server::WishListIntervalHandler;
|
use crate::message::server::WishListIntervalHandler;
|
||||||
use crate::message::Handlers;
|
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
use crate::message::MessageReader;
|
use crate::message::MessageReader;
|
||||||
use crate::peer::listen::Listen;
|
use crate::message::{Handlers, MessageType};
|
||||||
use crate::peer::ConnectionType;
|
use crate::peer::ConnectionType;
|
||||||
use crate::peer::Peer;
|
use crate::peer::Peer;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ use std::sync::{Arc, Barrier, Mutex};
|
|||||||
use std::thread::{self};
|
use std::thread::{self};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::{debug, error, info, warn};
|
use crate::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PeerAddress {
|
pub struct PeerAddress {
|
||||||
@@ -69,14 +70,6 @@ impl Context {
|
|||||||
logged_in: Option::None,
|
logged_in: Option::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn get_messages_for_user(&self, username: String) -> Option<&UserMessage> {
|
|
||||||
// self.user_messages.get(&username)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub fn get_rooms(&mut self) -> &mut Rooms {
|
|
||||||
// &mut self.rooms
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UserMessage {
|
pub struct UserMessage {
|
||||||
@@ -186,6 +179,14 @@ pub enum ServerOperation {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
ConnectToPeer(Peer),
|
ConnectToPeer(Peer),
|
||||||
PierceFirewall(u32),
|
PierceFirewall(u32),
|
||||||
|
GetPeerAddress(String),
|
||||||
|
GetPeerAddressResponse {
|
||||||
|
username: String,
|
||||||
|
host: String,
|
||||||
|
port: u32,
|
||||||
|
obfuscation_type: u32,
|
||||||
|
obfuscated_port: u16,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -195,11 +196,10 @@ pub struct Server {
|
|||||||
context: Arc<Mutex<Context>>,
|
context: Arc<Mutex<Context>>,
|
||||||
}
|
}
|
||||||
impl Server {
|
impl Server {
|
||||||
/// Create a new instance of Server, returning a Result
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
address: PeerAddress,
|
address: PeerAddress,
|
||||||
client_channel: Sender<ClientOperation>,
|
client_channel: Sender<ClientOperation>,
|
||||||
) -> Result<Self, io::Error> {
|
) -> std::result::Result<Self, io::Error> {
|
||||||
let (sender, server_channel): (
|
let (sender, server_channel): (
|
||||||
Sender<ServerOperation>,
|
Sender<ServerOperation>,
|
||||||
Receiver<ServerOperation>,
|
Receiver<ServerOperation>,
|
||||||
@@ -230,7 +230,7 @@ impl Server {
|
|||||||
&mut self,
|
&mut self,
|
||||||
server_channel: Receiver<ServerOperation>,
|
server_channel: Receiver<ServerOperation>,
|
||||||
client_channel: Sender<ClientOperation>,
|
client_channel: Sender<ClientOperation>,
|
||||||
) -> Result<(), io::Error> {
|
) -> std::result::Result<(), io::Error> {
|
||||||
let socket_address =
|
let socket_address =
|
||||||
format!("{}:{}", self.address.host, self.address.port)
|
format!("{}:{}", self.address.host, self.address.port)
|
||||||
.to_socket_addrs()?
|
.to_socket_addrs()?
|
||||||
@@ -279,9 +279,11 @@ impl Server {
|
|||||||
handlers.register_handler(ParentSpeedRatioHandler);
|
handlers.register_handler(ParentSpeedRatioHandler);
|
||||||
handlers.register_handler(PrivilegedUsersHandler);
|
handlers.register_handler(PrivilegedUsersHandler);
|
||||||
handlers.register_handler(FileSearchHandler);
|
handlers.register_handler(FileSearchHandler);
|
||||||
|
handlers.register_handler(GetPeerAddressHandler);
|
||||||
handlers.register_handler(ConnectToPeerHandler);
|
handlers.register_handler(ConnectToPeerHandler);
|
||||||
|
|
||||||
let dispatcher = MessageDispatcher::new(sender, handlers);
|
let dispatcher =
|
||||||
|
MessageDispatcher::new("server".to_string(), sender, handlers);
|
||||||
|
|
||||||
let mut buffered_reader = MessageReader::new();
|
let mut buffered_reader = MessageReader::new();
|
||||||
loop {
|
loop {
|
||||||
@@ -305,16 +307,16 @@ impl Server {
|
|||||||
|
|
||||||
match buffered_reader.extract_message() {
|
match buffered_reader.extract_message() {
|
||||||
Ok(Some(mut message)) => {
|
Ok(Some(mut message)) => {
|
||||||
// trace!(
|
trace!(
|
||||||
// "[server] ← {:?}",
|
"[server] ← {:?} - {}",
|
||||||
// message
|
message
|
||||||
// .get_message_name(
|
.get_message_name(
|
||||||
// MessageType::Server,
|
MessageType::Server,
|
||||||
// message.get_message_code() as u32
|
message.get_message_code_u32()
|
||||||
// )
|
)
|
||||||
// .map_err(|e| e.to_string())
|
.map_err(|e| e.to_string()),
|
||||||
// );
|
message.get_message_code_u32()
|
||||||
|
);
|
||||||
dispatcher.dispatch(&mut message)
|
dispatcher.dispatch(&mut message)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -325,10 +327,6 @@ impl Server {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// let monitor_handle = thread::spawn(move || loop {
|
|
||||||
// thread::sleep(Duration::from_secs(1));
|
|
||||||
// println!("Stream state: connected={}", stream.peer_addr().is_ok());
|
|
||||||
// });
|
|
||||||
let context = self.context.clone();
|
let context = self.context.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
write_barrier.wait();
|
write_barrier.wait();
|
||||||
@@ -336,72 +334,98 @@ impl Server {
|
|||||||
if let Ok(operation) = server_channel.recv() {
|
if let Ok(operation) = server_channel.recv() {
|
||||||
match operation {
|
match operation {
|
||||||
ServerOperation::ConnectToPeer(peer) => {
|
ServerOperation::ConnectToPeer(peer) => {
|
||||||
match peer.connection_type {
|
debug!(
|
||||||
|
"[server] ConnectToPeer {} - ConnectionType {}",
|
||||||
|
peer.username, peer.connection_type
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(op) = match peer.connection_type {
|
||||||
ConnectionType::P => {
|
ConnectionType::P => {
|
||||||
match client_channel.send(
|
Some(ClientOperation::ConnectToPeer(peer))
|
||||||
ClientOperation::ConnectToPeer(peer),
|
|
||||||
) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_e) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConnectionType::F => {
|
ConnectionType::F => {
|
||||||
match client_channel.send(
|
Some(ClientOperation::PierceFireWall(peer))
|
||||||
ClientOperation::PierceFireWall(peer),
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
debug!("Sent PierceFireWall operation for F-type connection");
|
|
||||||
}
|
|
||||||
Err(_e) => {
|
|
||||||
error!("Failed to send PierceFireWall operation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConnectionType::D => {}
|
ConnectionType::D => None,
|
||||||
|
} {
|
||||||
|
client_channel.send(op).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerOperation::LoginStatus(message) => {
|
ServerOperation::LoginStatus(message) => {
|
||||||
context.lock().unwrap().logged_in = Some(message);
|
context.lock().unwrap().logged_in = Some(message);
|
||||||
}
|
}
|
||||||
ServerOperation::PierceFirewall(token) => {
|
ServerOperation::PierceFirewall(token) => {
|
||||||
let pierce_message =
|
if let Err(e) = write_stream.write_all(
|
||||||
MessageFactory::build_pierce_firewall_message(
|
&MessageFactory::build_pierce_firewall_message(
|
||||||
token,
|
token,
|
||||||
|
)
|
||||||
|
.get_buffer(),
|
||||||
|
) {
|
||||||
|
error!(
|
||||||
|
"Error writing PierceFirewall message: {}",
|
||||||
|
e
|
||||||
);
|
);
|
||||||
match write_stream
|
break;
|
||||||
.write_all(&pierce_message.get_buffer())
|
|
||||||
{
|
|
||||||
Ok(_) => {
|
|
||||||
debug!("Sent PierceFirewall message with token: {}", token);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Error writing PierceFirewall message: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
debug!(
|
||||||
|
"Sent PierceFirewall message with token: {}",
|
||||||
|
token
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ServerOperation::SendMessage(message) => {
|
ServerOperation::SendMessage(message) => {
|
||||||
// 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())
|
match write_stream.write_all(&message.get_buffer())
|
||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => trace!(
|
||||||
Err(e) => {
|
"[server] → {:?} - {:}",
|
||||||
error!(
|
message
|
||||||
"Error writing message to stream : {}",
|
.get_message_name(
|
||||||
e
|
MessageType::Server,
|
||||||
);
|
message.get_message_code_send()
|
||||||
break;
|
as u32
|
||||||
}
|
)
|
||||||
|
.map_err(|e| e.to_string()),
|
||||||
|
message.get_message_code_send() as u32
|
||||||
|
),
|
||||||
|
Err(e) => error!(
|
||||||
|
"Error writing message to server: {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerOperation::GetPeerAddress(username) => {
|
||||||
|
if let Err(e) = write_stream.write_all(
|
||||||
|
&MessageFactory::build_get_peer_address(
|
||||||
|
&username,
|
||||||
|
)
|
||||||
|
.get_buffer(),
|
||||||
|
) {
|
||||||
|
error!("Error writing get_peeer_address message: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerOperation::GetPeerAddressResponse {
|
||||||
|
username,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
obfuscation_type,
|
||||||
|
obfuscated_port,
|
||||||
|
} => {
|
||||||
|
debug!(
|
||||||
|
"Received GetPeerAddress response for {}: {}:{} (obf_type: {}, obf_port: {})",
|
||||||
|
username, host, port, obfuscation_type, obfuscated_port
|
||||||
|
);
|
||||||
|
|
||||||
|
// Forward the response to the client channel
|
||||||
|
if let Err(e) = client_channel.send(
|
||||||
|
ClientOperation::GetPeerAddressResponse {
|
||||||
|
username,
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
obfuscation_type,
|
||||||
|
obfuscated_port,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
error!("Error forwarding GetPeerAddress response to client: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,16 +442,7 @@ impl Server {
|
|||||||
Err(e) => error!("Failed to send: {}", e),
|
Err(e) => error!("Failed to send: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn start_listener(&self, server_channel: Sender<ServerOperation>) {
|
pub fn login(&self, username: &str, password: &str) -> Result<bool> {
|
||||||
thread::spawn(move || Listen::start(2234, server_channel));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn login(
|
|
||||||
&self,
|
|
||||||
username: &str,
|
|
||||||
password: &str,
|
|
||||||
) -> Result<bool, std::io::Error> {
|
|
||||||
self.start_listener(self.sender.clone());
|
|
||||||
// Send the login message
|
// Send the login message
|
||||||
self.queue_message(MessageFactory::build_login_message(
|
self.queue_message(MessageFactory::build_login_message(
|
||||||
username, password,
|
username, password,
|
||||||
@@ -441,10 +456,7 @@ impl Server {
|
|||||||
loop {
|
loop {
|
||||||
if start.elapsed() > timeout {
|
if start.elapsed() > timeout {
|
||||||
warn!("Timeout waiting for login response");
|
warn!("Timeout waiting for login response");
|
||||||
return Err(std::io::Error::new(
|
return Err(SoulseekRs::Timeout);
|
||||||
std::io::ErrorKind::TimedOut,
|
|
||||||
"Timeout waiting for login response",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -458,12 +470,14 @@ impl Server {
|
|||||||
|
|
||||||
if logged_in.unwrap() {
|
if logged_in.unwrap() {
|
||||||
info!("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(
|
self.queue_message(MessageFactory::build_shared_folders_message(
|
||||||
1, 499,
|
1, 499,
|
||||||
));
|
));
|
||||||
self.queue_message(MessageFactory::build_no_parent_message());
|
self.queue_message(MessageFactory::build_no_parent_message());
|
||||||
self.queue_message(MessageFactory::build_set_status_message(2));
|
self.queue_message(MessageFactory::build_set_status_message(2));
|
||||||
|
self.queue_message(MessageFactory::build_set_wait_port_message(
|
||||||
|
2235,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(logged_in.unwrap())
|
Ok(logged_in.unwrap())
|
||||||
|
|||||||
+16
-8
@@ -1,13 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{message::Message, utils::zlib::deflate};
|
use crate::{error::Result, message::Message, utils::zlib::deflate};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub size: u32,
|
pub size: u64,
|
||||||
pub attribs: HashMap<u32, u32>,
|
pub attribs: HashMap<u32, u32>,
|
||||||
}
|
}
|
||||||
pub struct UploadFailed {
|
pub struct UploadFailed {
|
||||||
@@ -27,14 +27,15 @@ pub struct FileSearchResult {
|
|||||||
pub files: Vec<File>,
|
pub files: Vec<File>,
|
||||||
pub slots: u8,
|
pub slots: u8,
|
||||||
pub speed: u32,
|
pub speed: u32,
|
||||||
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSearchResult {
|
impl FileSearchResult {
|
||||||
pub fn new_from_message(message: &mut Message) -> Self {
|
pub fn new_from_message(message: &mut Message) -> Result<Self> {
|
||||||
let pointer = message.get_pointer();
|
let pointer = message.get_pointer();
|
||||||
let size = message.get_size();
|
let size = message.get_size();
|
||||||
let data: Vec<u8> = message.get_slice(pointer, size);
|
let data: Vec<u8> = message.get_slice(pointer, size);
|
||||||
let deflated = deflate(&data).unwrap();
|
let deflated = deflate(&data)?;
|
||||||
let mut message = Message::new_with_data(deflated);
|
let mut message = Message::new_with_data(deflated);
|
||||||
|
|
||||||
let username = message.read_string();
|
let username = message.read_string();
|
||||||
@@ -44,8 +45,7 @@ impl FileSearchResult {
|
|||||||
for _ in 0..n_files {
|
for _ in 0..n_files {
|
||||||
message.read_int8();
|
message.read_int8();
|
||||||
let name = message.read_string();
|
let name = message.read_string();
|
||||||
let size = message.read_int32();
|
let size = message.read_int64();
|
||||||
message.read_int32();
|
|
||||||
message.read_string();
|
message.read_string();
|
||||||
let n_attribs = message.read_int32();
|
let n_attribs = message.read_int32();
|
||||||
let mut attribs: HashMap<u32, u32> = HashMap::new();
|
let mut attribs: HashMap<u32, u32> = HashMap::new();
|
||||||
@@ -63,12 +63,13 @@ impl FileSearchResult {
|
|||||||
let slots = message.read_int8();
|
let slots = message.read_int8();
|
||||||
let speed = message.read_int32();
|
let speed = message.read_int32();
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
token,
|
token,
|
||||||
files,
|
files,
|
||||||
slots,
|
slots,
|
||||||
speed,
|
speed,
|
||||||
}
|
username,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +81,13 @@ pub struct Transfer {
|
|||||||
pub filename: String,
|
pub filename: String,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Download {
|
||||||
|
pub username: String,
|
||||||
|
pub filename: String,
|
||||||
|
pub token: u32,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -6,6 +7,7 @@ type Job = Box<dyn FnOnce() + Send + 'static>;
|
|||||||
pub struct ThreadPool {
|
pub struct ThreadPool {
|
||||||
workers: Vec<Worker>,
|
workers: Vec<Worker>,
|
||||||
sender: mpsc::Sender<Job>,
|
sender: mpsc::Sender<Job>,
|
||||||
|
active_threads: Arc<AtomicUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadPool {
|
impl ThreadPool {
|
||||||
@@ -14,13 +16,23 @@ impl ThreadPool {
|
|||||||
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
let receiver = Arc::new(Mutex::new(receiver));
|
let receiver = Arc::new(Mutex::new(receiver));
|
||||||
|
let active_threads = Arc::new(AtomicUsize::new(0));
|
||||||
let mut workers = Vec::with_capacity(size);
|
let mut workers = Vec::with_capacity(size);
|
||||||
|
|
||||||
for id in 0..size {
|
for id in 0..size {
|
||||||
workers.push(Worker::new(id, Arc::clone(&receiver)));
|
workers.push(Worker::new(
|
||||||
|
id,
|
||||||
|
Arc::clone(&receiver),
|
||||||
|
Arc::clone(&active_threads),
|
||||||
|
size,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool { workers, sender }
|
ThreadPool {
|
||||||
|
workers,
|
||||||
|
sender,
|
||||||
|
active_threads,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<F>(&self, f: F)
|
pub fn execute<F>(&self, f: F)
|
||||||
@@ -30,6 +42,14 @@ impl ThreadPool {
|
|||||||
let job = Box::new(f);
|
let job = Box::new(f);
|
||||||
self.sender.send(job).unwrap();
|
self.sender.send(job).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn thread_count(&self) -> usize {
|
||||||
|
self.workers.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_thread_count(&self) -> usize {
|
||||||
|
self.active_threads.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ThreadPool {
|
impl Drop for ThreadPool {
|
||||||
@@ -48,12 +68,35 @@ struct Worker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Worker {
|
impl Worker {
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
fn new(
|
||||||
|
id: usize,
|
||||||
|
receiver: Arc<Mutex<mpsc::Receiver<Job>>>,
|
||||||
|
active_threads: Arc<AtomicUsize>,
|
||||||
|
total_threads: usize,
|
||||||
|
) -> Worker {
|
||||||
let thread = thread::spawn(move || loop {
|
let thread = thread::spawn(move || loop {
|
||||||
let job = receiver.lock().unwrap().recv();
|
let job = receiver.lock().unwrap().recv();
|
||||||
match job {
|
match job {
|
||||||
Ok(job) => {
|
Ok(job) => {
|
||||||
|
let active =
|
||||||
|
active_threads.fetch_add(1, Ordering::SeqCst) + 1;
|
||||||
|
trace!(
|
||||||
|
"Thread {} started job (active: {}/{})",
|
||||||
|
id,
|
||||||
|
active,
|
||||||
|
total_threads
|
||||||
|
);
|
||||||
|
|
||||||
job();
|
job();
|
||||||
|
|
||||||
|
let active =
|
||||||
|
active_threads.fetch_sub(1, Ordering::SeqCst) - 1;
|
||||||
|
trace!(
|
||||||
|
"Thread {} finished job (active: {}/{})",
|
||||||
|
id,
|
||||||
|
active,
|
||||||
|
total_threads
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
break;
|
break;
|
||||||
|
|||||||
+26
-17
@@ -17,7 +17,7 @@ impl BitReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&mut self) -> Result<u8, String> {
|
fn read_byte(&mut self) -> std::result::Result<u8, String> {
|
||||||
self.numbits = 0; // discard unread bits
|
self.numbits = 0; // discard unread bits
|
||||||
if self.pos >= self.mem.len() {
|
if self.pos >= self.mem.len() {
|
||||||
return Err("End of data".to_string());
|
return Err("End of data".to_string());
|
||||||
@@ -27,7 +27,7 @@ impl BitReader {
|
|||||||
Ok(b)
|
Ok(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bit(&mut self) -> Result<u8, String> {
|
fn read_bit(&mut self) -> std::result::Result<u8, String> {
|
||||||
if self.numbits <= 0 {
|
if self.numbits <= 0 {
|
||||||
self.b = self.read_byte()?;
|
self.b = self.read_byte()?;
|
||||||
self.numbits = 8;
|
self.numbits = 8;
|
||||||
@@ -39,7 +39,7 @@ impl BitReader {
|
|||||||
Ok(bit)
|
Ok(bit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bits(&mut self, n: usize) -> Result<u32, String> {
|
fn read_bits(&mut self, n: usize) -> std::result::Result<u32, String> {
|
||||||
let mut o = 0u32;
|
let mut o = 0u32;
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
o |= (self.read_bit()? as u32) << i;
|
o |= (self.read_bit()? as u32) << i;
|
||||||
@@ -47,7 +47,7 @@ impl BitReader {
|
|||||||
Ok(o)
|
Ok(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_bytes(&mut self, n: usize) -> Result<u32, String> {
|
fn read_bytes(&mut self, n: usize) -> std::result::Result<u32, String> {
|
||||||
// read bytes as an integer in little-endian
|
// read bytes as an integer in little-endian
|
||||||
let mut o = 0u32;
|
let mut o = 0u32;
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
@@ -57,32 +57,38 @@ impl BitReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deflate(input: &[u8]) -> Result<Vec<u8>, String> {
|
use crate::error::{Result, SoulseekRs};
|
||||||
|
|
||||||
|
pub fn deflate(input: &[u8]) -> Result<Vec<u8>> {
|
||||||
let mut r = BitReader::new(input.to_vec());
|
let mut r = BitReader::new(input.to_vec());
|
||||||
let cmf = r.read_byte()?;
|
let cmf = r.read_byte()?;
|
||||||
let cm = cmf & 15; // Compression method
|
let cm = cmf & 15; // Compression method
|
||||||
if cm != 8 {
|
if cm != 8 {
|
||||||
// only CM=8 is supported
|
// only CM=8 is supported
|
||||||
return Err("invalid CM".to_string());
|
return Err(SoulseekRs::CompressionError("invalid CM".to_string()));
|
||||||
}
|
}
|
||||||
let cinfo = (cmf >> 4) & 15; // Compression info
|
let cinfo = (cmf >> 4) & 15; // Compression info
|
||||||
if cinfo > 7 {
|
if cinfo > 7 {
|
||||||
return Err("invalid CINFO".to_string());
|
return Err(SoulseekRs::CompressionError("invalid CINFO".to_string()));
|
||||||
}
|
}
|
||||||
let flg = r.read_byte()?;
|
let flg = r.read_byte()?;
|
||||||
if ((cmf as u32) * 256 + (flg as u32)) % 31 != 0 {
|
if ((cmf as u32) * 256 + (flg as u32)) % 31 != 0 {
|
||||||
return Err("CMF+FLG checksum failed".to_string());
|
return Err(SoulseekRs::CompressionError(
|
||||||
|
"CMF+FLG checksum failed".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let fdict = (flg >> 5) & 1; // preset dictionary?
|
let fdict = (flg >> 5) & 1; // preset dictionary?
|
||||||
if fdict != 0 {
|
if fdict != 0 {
|
||||||
return Err("preset dictionary not supported".to_string());
|
return Err(SoulseekRs::CompressionError(
|
||||||
|
"preset dictionary not supported".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let out = inflate(&mut r)?; // decompress DEFLATE data
|
let out = inflate(&mut r).map_err(SoulseekRs::CompressionError)?; // decompress DEFLATE data
|
||||||
let _adler32 = r.read_bytes(4)?; // Adler-32 checksum (for this exercise, we ignore it)
|
let _adler32 = r.read_bytes(4)?; // Adler-32 checksum (for this exercise, we ignore it)
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inflate(r: &mut BitReader) -> Result<Vec<u8>, String> {
|
fn inflate(r: &mut BitReader) -> std::result::Result<Vec<u8>, String> {
|
||||||
let mut bfinal = 0;
|
let mut bfinal = 0;
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
while bfinal == 0 {
|
while bfinal == 0 {
|
||||||
@@ -101,7 +107,7 @@ fn inflate(r: &mut BitReader) -> Result<Vec<u8>, String> {
|
|||||||
fn inflate_block_no_compression(
|
fn inflate_block_no_compression(
|
||||||
r: &mut BitReader,
|
r: &mut BitReader,
|
||||||
o: &mut Vec<u8>,
|
o: &mut Vec<u8>,
|
||||||
) -> Result<(), String> {
|
) -> std::result::Result<(), String> {
|
||||||
let len = r.read_bytes(2)?;
|
let len = r.read_bytes(2)?;
|
||||||
let _nlen = r.read_bytes(2)?;
|
let _nlen = r.read_bytes(2)?;
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
@@ -157,7 +163,10 @@ impl HuffmanTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_symbol(r: &mut BitReader, t: &HuffmanTree) -> Result<u32, String> {
|
fn decode_symbol(
|
||||||
|
r: &mut BitReader,
|
||||||
|
t: &HuffmanTree,
|
||||||
|
) -> std::result::Result<u32, String> {
|
||||||
let mut node = &t.root;
|
let mut node = &t.root;
|
||||||
while node.left.is_some() || node.right.is_some() {
|
while node.left.is_some() || node.right.is_some() {
|
||||||
let b = r.read_bit()?;
|
let b = r.read_bit()?;
|
||||||
@@ -192,7 +201,7 @@ fn inflate_block_data(
|
|||||||
literal_length_tree: &HuffmanTree,
|
literal_length_tree: &HuffmanTree,
|
||||||
distance_tree: &HuffmanTree,
|
distance_tree: &HuffmanTree,
|
||||||
out: &mut Vec<u8>,
|
out: &mut Vec<u8>,
|
||||||
) -> Result<(), String> {
|
) -> std::result::Result<(), String> {
|
||||||
loop {
|
loop {
|
||||||
let sym = decode_symbol(r, literal_length_tree)?;
|
let sym = decode_symbol(r, literal_length_tree)?;
|
||||||
if sym <= 255 {
|
if sym <= 255 {
|
||||||
@@ -257,7 +266,7 @@ const CODE_LENGTH_CODES_ORDER: [usize; 19] = [
|
|||||||
|
|
||||||
fn decode_trees(
|
fn decode_trees(
|
||||||
r: &mut BitReader,
|
r: &mut BitReader,
|
||||||
) -> Result<(HuffmanTree, HuffmanTree), String> {
|
) -> std::result::Result<(HuffmanTree, HuffmanTree), String> {
|
||||||
// The number of literal/length codes
|
// The number of literal/length codes
|
||||||
let hlit = r.read_bits(5)? + 257;
|
let hlit = r.read_bits(5)? + 257;
|
||||||
|
|
||||||
@@ -325,7 +334,7 @@ fn decode_trees(
|
|||||||
fn inflate_block_dynamic(
|
fn inflate_block_dynamic(
|
||||||
r: &mut BitReader,
|
r: &mut BitReader,
|
||||||
o: &mut Vec<u8>,
|
o: &mut Vec<u8>,
|
||||||
) -> Result<(), String> {
|
) -> std::result::Result<(), String> {
|
||||||
let (literal_length_tree, distance_tree) = decode_trees(r)?;
|
let (literal_length_tree, distance_tree) = decode_trees(r)?;
|
||||||
inflate_block_data(r, &literal_length_tree, &distance_tree, o)
|
inflate_block_data(r, &literal_length_tree, &distance_tree, o)
|
||||||
}
|
}
|
||||||
@@ -333,7 +342,7 @@ fn inflate_block_dynamic(
|
|||||||
fn inflate_block_fixed(
|
fn inflate_block_fixed(
|
||||||
r: &mut BitReader,
|
r: &mut BitReader,
|
||||||
o: &mut Vec<u8>,
|
o: &mut Vec<u8>,
|
||||||
) -> Result<(), String> {
|
) -> std::result::Result<(), String> {
|
||||||
let mut bl = Vec::new();
|
let mut bl = Vec::new();
|
||||||
bl.extend(vec![8; 144]); // 0-143: 8 bits
|
bl.extend(vec![8; 144]); // 0-143: 8 bits
|
||||||
bl.extend(vec![9; 112]); // 144-255: 9 bits
|
bl.extend(vec![9; 112]); // 144-255: 9 bits
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[2025-10-01 13:30:27] [Conn] Sending message of type <class 'pynicotine.slskmessages.FileSearchResponse'> to user insane_in_the_brain2 on new connection
|
||||||
|
[2025-10-01 13:30:27] [Msg] OUT: <S - GetPeerAddress> {'user': 'insane_in_the_brain2', 'ip_address': None, 'port': None, 'obfuscation_type': None, 'obfuscated_port': None}
|
||||||
|
[2025-10-01 13:30:27] [Conn] Requesting address for user insane_in_the_brain2
|
||||||
|
[2025-10-01 13:30:27] [Conn] Attempting direct connection of type P to user insane_in_the_brain2, address ('127.0.0.1', 53507)
|
||||||
|
[2025-10-01 13:30:27] [Msg] OUT: <S - ConnectToPeer> {'token': 567916, 'user': 'insane_in_the_brain2', 'conn_type': 'P', 'ip_address': None, 'port': None, 'privileged': None, 'obfuscation_type': None, 'obfuscated_port': None}
|
||||||
|
[2025-10-01 13:30:27] [Conn] Requesting indirect connection to user insane_in_the_brain2 with token 567916
|
||||||
|
[2025-10-01 13:30:27] [Msg] IN: <S - GetPeerAddress> {'user': 'insane_in_the_brain2', 'ip_address': '127.0.0.1', 'port': 53507, 'obfuscation_type': 1, 'obfuscated_port': 53508}
|
||||||
|
[2025-10-01 13:30:27] [Conn] Established outgoing connection of type P with user insane_in_the_brain2. List of outgoing messages: [<pynicotine.slskmessages.FileSearchResponse object at 0x10902f340>]
|
||||||
|
[2025-10-01 13:30:27] [Conn] Sending peer init message of type P to user insane_in_the_brain2
|
||||||
|
[2025-10-01 13:30:27] [Msg] OUT: <I - PeerInit> {'sock': <socket.socket fd=16, family=2, type=1, proto=0, laddr=('127.0.0.1', 55594), raddr=('127.0.0.1', 53507)>, 'init_user': 'insane_in_the_brain6', 'target_user': 'insane_in_the_brain2', 'conn_type': 'P', 'outgoing_msgs': [<pynicotine.slskmessages.FileSearchResponse object at 0x10902f340>], 'token': 0}
|
||||||
|
[2025-10-01 13:30:27] [Msg] OUT: <P - FileSearchResponse> {'search_username': 'insane_in_the_brain6', 'token': 1, 'freeulslots': True, 'ulspeed': 0, 'inqueue': 0, 'unknown': 0}
|
||||||
Reference in New Issue
Block a user