|
@@ -1,7 +1,7 @@
|
|
// Code specific to the client in the client-server mode.
|
|
// Code specific to the client in the client-server mode.
|
|
|
|
|
|
use mgen::updater::Updater;
|
|
use mgen::updater::Updater;
|
|
-use mgen::{HandshakeRef, MessageHeader, SerializedMessage};
|
|
+use mgen::{log, HandshakeRef, MessageHeader, SerializedMessage};
|
|
use rand_xoshiro::{rand_core::SeedableRng, Xoshiro256PlusPlus};
|
|
use rand_xoshiro::{rand_core::SeedableRng, Xoshiro256PlusPlus};
|
|
use serde::Deserialize;
|
|
use serde::Deserialize;
|
|
use std::result::Result;
|
|
use std::result::Result;
|
|
@@ -40,6 +40,10 @@ type WriteSocketUpdaterOut = Updater<WriteHalf<TlsStream<TcpStream>>>;
|
|
type ErrorChannelIn = mpsc::UnboundedSender<MessengerError>;
|
|
type ErrorChannelIn = mpsc::UnboundedSender<MessengerError>;
|
|
/// Type for getting errors from other threads.
|
|
/// Type for getting errors from other threads.
|
|
type ErrorChannelOut = mpsc::UnboundedReceiver<MessengerError>;
|
|
type ErrorChannelOut = mpsc::UnboundedReceiver<MessengerError>;
|
|
|
|
+/// Type for sending sizes to the attachment sender thread.
|
|
|
|
+type SizeChannelIn = mpsc::UnboundedSender<usize>;
|
|
|
|
+/// Type for getting sizes from other threads.
|
|
|
|
+type SizeChannelOut = mpsc::UnboundedReceiver<usize>;
|
|
|
|
|
|
// we gain a (very) tiny performance win by not bothering to validate the cert
|
|
// we gain a (very) tiny performance win by not bothering to validate the cert
|
|
struct NoCertificateVerification {}
|
|
struct NoCertificateVerification {}
|
|
@@ -62,24 +66,174 @@ impl tokio_rustls::rustls::client::ServerCertVerifier for NoCertificateVerificat
|
|
/// checking for any network errors while doing so,
|
|
/// checking for any network errors while doing so,
|
|
/// and giving messages to the state thread.
|
|
/// and giving messages to the state thread.
|
|
async fn reader(
|
|
async fn reader(
|
|
|
|
+ web_params: SocksParams,
|
|
|
|
+ retry: Duration,
|
|
|
|
+ tls_config: tokio_rustls::rustls::ClientConfig,
|
|
message_channel: ReaderToState,
|
|
message_channel: ReaderToState,
|
|
- mut socket_updater: ReadSocketUpdaterOut,
|
|
+ socket_updater: ReadSocketUpdaterOut,
|
|
error_channel: ErrorChannelIn,
|
|
error_channel: ErrorChannelIn,
|
|
) {
|
|
) {
|
|
- loop {
|
|
+ let https = hyper_rustls::HttpsConnectorBuilder::new()
|
|
- let mut stream = socket_updater.recv().await;
|
|
+ .with_tls_config(tls_config)
|
|
|
|
+ .https_only()
|
|
|
|
+ .enable_http1()
|
|
|
|
+ .build();
|
|
|
|
+
|
|
|
|
+ match web_params.socks {
|
|
|
|
+ Some(proxy) => {
|
|
|
|
+ let auth = hyper_socks2::Auth {
|
|
|
|
+ username: web_params.user.clone(),
|
|
|
|
+ password: web_params.recipient,
|
|
|
|
+ };
|
|
|
|
+ let socks = hyper_socks2::SocksConnector {
|
|
|
|
+ proxy_addr: proxy.parse().expect("Invalid proxy URI"),
|
|
|
|
+ auth: Some(auth),
|
|
|
|
+ connector: https,
|
|
|
|
+ };
|
|
|
|
+ let client: hyper::Client<_, hyper::Body> = hyper::Client::builder().build(socks);
|
|
|
|
+ worker(
|
|
|
|
+ web_params.target,
|
|
|
|
+ web_params.user,
|
|
|
|
+ retry,
|
|
|
|
+ client,
|
|
|
|
+ message_channel,
|
|
|
|
+ socket_updater,
|
|
|
|
+ error_channel,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ None => {
|
|
|
|
+ let client = hyper::Client::builder().build(https);
|
|
|
|
+ worker(
|
|
|
|
+ web_params.target,
|
|
|
|
+ web_params.user,
|
|
|
|
+ retry,
|
|
|
|
+ client,
|
|
|
|
+ message_channel,
|
|
|
|
+ socket_updater,
|
|
|
|
+ error_channel,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ async fn worker<C>(
|
|
|
|
+ target: String,
|
|
|
|
+ user: String,
|
|
|
|
+ retry: Duration,
|
|
|
|
+ client: hyper::Client<C, hyper::Body>,
|
|
|
|
+ message_channel: ReaderToState,
|
|
|
|
+ mut socket_updater: ReadSocketUpdaterOut,
|
|
|
|
+ error_channel: ErrorChannelIn,
|
|
|
|
+ ) where
|
|
|
|
+ C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
|
|
|
+ {
|
|
loop {
|
|
loop {
|
|
- let msg = match mgen::get_message(&mut stream).await {
|
|
+ let mut message_stream = socket_updater.recv().await;
|
|
- Ok(msg) => msg,
|
|
+
|
|
- Err(e) => {
|
|
+ loop {
|
|
- error_channel.send(e.into()).expect("Error channel closed");
|
|
+ let msg = match mgen::get_message(&mut message_stream).await {
|
|
- break;
|
|
+ Ok(msg) => msg,
|
|
|
|
+ Err(e) => {
|
|
|
|
+ error_channel.send(e.into()).expect("Error channel closed");
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if msg.body.has_attachment() {
|
|
|
|
+ let url: hyper::Uri =
|
|
|
|
+ format!("{}/?size={}&user={}", target, msg.body.total_size(), user)
|
|
|
|
+ .parse()
|
|
|
|
+ .expect("Invalid URI");
|
|
|
|
+ let client = client.clone();
|
|
|
|
+ tokio::spawn(async move {
|
|
|
|
+ let mut res = client.get(url.clone()).await;
|
|
|
|
+ while res.is_err() {
|
|
|
|
+ log!("Error fetching: {}", res.unwrap_err());
|
|
|
|
+ tokio::time::sleep(retry).await;
|
|
|
|
+ res = client.get(url.clone()).await;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ message_channel
|
|
|
|
+ .send(msg)
|
|
|
|
+ .expect("Reader message channel closed");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async fn uploader(
|
|
|
|
+ web_params: SocksParams,
|
|
|
|
+ retry: Duration,
|
|
|
|
+ tls_config: tokio_rustls::rustls::ClientConfig,
|
|
|
|
+ size_channel: SizeChannelOut,
|
|
|
|
+) {
|
|
|
|
+ let https = hyper_rustls::HttpsConnectorBuilder::new()
|
|
|
|
+ .with_tls_config(tls_config)
|
|
|
|
+ .https_only()
|
|
|
|
+ .enable_http1()
|
|
|
|
+ .build();
|
|
|
|
+
|
|
|
|
+ match web_params.socks {
|
|
|
|
+ Some(proxy) => {
|
|
|
|
+ let auth = hyper_socks2::Auth {
|
|
|
|
+ username: web_params.user.clone(),
|
|
|
|
+ password: web_params.recipient,
|
|
|
|
+ };
|
|
|
|
+ let socks = hyper_socks2::SocksConnector {
|
|
|
|
+ proxy_addr: proxy.parse().expect("Invalid proxy URI"),
|
|
|
|
+ auth: Some(auth),
|
|
|
|
+ connector: https,
|
|
};
|
|
};
|
|
|
|
+ let client = hyper::Client::builder().build(socks);
|
|
|
|
+ worker(
|
|
|
|
+ web_params.target,
|
|
|
|
+ web_params.user,
|
|
|
|
+ retry,
|
|
|
|
+ client,
|
|
|
|
+ size_channel,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ None => {
|
|
|
|
+ let client = hyper::Client::builder().build(https);
|
|
|
|
+ worker(
|
|
|
|
+ web_params.target,
|
|
|
|
+ web_params.user,
|
|
|
|
+ retry,
|
|
|
|
+ client,
|
|
|
|
+ size_channel,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- message_channel
|
|
+ async fn worker<C>(
|
|
- .send(msg)
|
|
+ target: String,
|
|
- .expect("Reader message channel closed");
|
|
+ user: String,
|
|
|
|
+ retry: Duration,
|
|
|
|
+ client: hyper::Client<C, hyper::Body>,
|
|
|
|
+ mut size_channel: SizeChannelOut,
|
|
|
|
+ ) where
|
|
|
|
+ C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
|
|
|
+ {
|
|
|
|
+ loop {
|
|
|
|
+ let size = size_channel.recv().await.expect("Size channel closed");
|
|
|
|
+ let client = client.clone();
|
|
|
|
+ let url: hyper::Uri = format!("{}/?size={}&user={}", target, size, user)
|
|
|
|
+ .parse()
|
|
|
|
+ .expect("Invalid URI");
|
|
|
|
+ let request = hyper::Request::put(url.clone())
|
|
|
|
+ .body(hyper::Body::empty())
|
|
|
|
+ .expect("Invalid HTTP request attempted to construct");
|
|
|
|
+ let mut res = client.request(request).await;
|
|
|
|
+ while res.is_err() {
|
|
|
|
+ log!("Error uploading: {}", res.unwrap_err());
|
|
|
|
+ tokio::time::sleep(retry).await;
|
|
|
|
+ res = client.get(url.clone()).await;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -88,6 +242,7 @@ async fn reader(
|
|
/// and checking for any network errors while doing so.
|
|
/// and checking for any network errors while doing so.
|
|
async fn writer(
|
|
async fn writer(
|
|
mut message_channel: WriterFromState,
|
|
mut message_channel: WriterFromState,
|
|
|
|
+ attachment_channel: SizeChannelIn,
|
|
mut socket_updater: WriteSocketUpdaterOut,
|
|
mut socket_updater: WriteSocketUpdaterOut,
|
|
error_channel: ErrorChannelIn,
|
|
error_channel: ErrorChannelIn,
|
|
) {
|
|
) {
|
|
@@ -98,6 +253,13 @@ async fn writer(
|
|
.recv()
|
|
.recv()
|
|
.await
|
|
.await
|
|
.expect("Writer message channel closed");
|
|
.expect("Writer message channel closed");
|
|
|
|
+
|
|
|
|
+ if msg.body.has_attachment() {
|
|
|
|
+ attachment_channel
|
|
|
|
+ .send(msg.body.total_size())
|
|
|
|
+ .expect("Attachment channel closed");
|
|
|
|
+ }
|
|
|
|
+
|
|
if let Err(e) = msg.write_all_to(&mut stream).await {
|
|
if let Err(e) = msg.write_all_to(&mut stream).await {
|
|
error_channel.send(e.into()).expect("Error channel closed");
|
|
error_channel.send(e.into()).expect("Error channel closed");
|
|
break;
|
|
break;
|
|
@@ -110,17 +272,12 @@ async fn writer(
|
|
/// and determining how to handle errors this or other threads receive.
|
|
/// and determining how to handle errors this or other threads receive.
|
|
async fn socket_updater(
|
|
async fn socket_updater(
|
|
str_params: SocksParams,
|
|
str_params: SocksParams,
|
|
- retry: f64,
|
|
+ retry: Duration,
|
|
|
|
+ tls_config: tokio_rustls::rustls::ClientConfig,
|
|
mut error_channel: ErrorChannelOut,
|
|
mut error_channel: ErrorChannelOut,
|
|
reader_channel: ReadSocketUpdaterIn,
|
|
reader_channel: ReadSocketUpdaterIn,
|
|
writer_channel: WriteSocketUpdaterIn,
|
|
writer_channel: WriteSocketUpdaterIn,
|
|
) -> FatalError {
|
|
) -> FatalError {
|
|
- let retry = Duration::from_secs_f64(retry);
|
|
|
|
-
|
|
|
|
- let tls_config = tokio_rustls::rustls::ClientConfig::builder()
|
|
|
|
- .with_safe_defaults()
|
|
|
|
- .with_custom_certificate_verifier(Arc::new(NoCertificateVerification {}))
|
|
|
|
- .with_no_client_auth();
|
|
|
|
let connector = TlsConnector::from(Arc::new(tls_config));
|
|
let connector = TlsConnector::from(Arc::new(tls_config));
|
|
|
|
|
|
// unwrap is safe, split always returns at least one element
|
|
// unwrap is safe, split always returns at least one element
|
|
@@ -176,9 +333,16 @@ async fn manage_conversation(
|
|
let mut rng = Xoshiro256PlusPlus::from_entropy();
|
|
let mut rng = Xoshiro256PlusPlus::from_entropy();
|
|
let distributions: Distributions = config.distributions.try_into()?;
|
|
let distributions: Distributions = config.distributions.try_into()?;
|
|
|
|
|
|
- let str_params = SocksParams {
|
|
+ let message_server_params = SocksParams {
|
|
|
|
+ socks: socks.clone(),
|
|
|
|
+ target: config.message_server,
|
|
|
|
+ user: user.clone(),
|
|
|
|
+ recipient: config.group.clone(),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let web_server_params = SocksParams {
|
|
socks,
|
|
socks,
|
|
- target: config.server,
|
|
+ target: config.web_server,
|
|
user: user.clone(),
|
|
user: user.clone(),
|
|
recipient: config.group.clone(),
|
|
recipient: config.group.clone(),
|
|
};
|
|
};
|
|
@@ -192,16 +356,38 @@ async fn manage_conversation(
|
|
let write_socket_updater_in = Updater::new();
|
|
let write_socket_updater_in = Updater::new();
|
|
let write_socket_updater_out = write_socket_updater_in.clone();
|
|
let write_socket_updater_out = write_socket_updater_in.clone();
|
|
let (errs_in, errs_out) = mpsc::unbounded_channel();
|
|
let (errs_in, errs_out) = mpsc::unbounded_channel();
|
|
|
|
+ let (writer_to_uploader, uploader_from_writer) = mpsc::unbounded_channel();
|
|
|
|
+
|
|
|
|
+ let retry = Duration::from_secs_f64(config.retry);
|
|
|
|
+ let tls_config = tokio_rustls::rustls::ClientConfig::builder()
|
|
|
|
+ .with_safe_defaults()
|
|
|
|
+ .with_custom_certificate_verifier(Arc::new(NoCertificateVerification {}))
|
|
|
|
+ .with_no_client_auth();
|
|
|
|
|
|
tokio::spawn(reader(
|
|
tokio::spawn(reader(
|
|
|
|
+ web_server_params.clone(),
|
|
|
|
+ retry,
|
|
|
|
+ tls_config.clone(),
|
|
reader_to_state,
|
|
reader_to_state,
|
|
read_socket_updater_out,
|
|
read_socket_updater_out,
|
|
errs_in.clone(),
|
|
errs_in.clone(),
|
|
));
|
|
));
|
|
- tokio::spawn(writer(writer_from_state, write_socket_updater_out, errs_in));
|
|
+ tokio::spawn(writer(
|
|
|
|
+ writer_from_state,
|
|
|
|
+ writer_to_uploader,
|
|
|
|
+ write_socket_updater_out,
|
|
|
|
+ errs_in,
|
|
|
|
+ ));
|
|
|
|
+ tokio::spawn(uploader(
|
|
|
|
+ web_server_params,
|
|
|
|
+ retry,
|
|
|
|
+ tls_config.clone(),
|
|
|
|
+ uploader_from_writer,
|
|
|
|
+ ));
|
|
tokio::spawn(socket_updater(
|
|
tokio::spawn(socket_updater(
|
|
- str_params,
|
|
+ message_server_params,
|
|
- config.retry,
|
|
+ retry,
|
|
|
|
+ tls_config,
|
|
errs_out,
|
|
errs_out,
|
|
read_socket_updater_in,
|
|
read_socket_updater_in,
|
|
write_socket_updater_in,
|
|
write_socket_updater_in,
|
|
@@ -244,7 +430,8 @@ async fn manage_conversation(
|
|
#[derive(Debug, Deserialize)]
|
|
#[derive(Debug, Deserialize)]
|
|
struct ConversationConfig {
|
|
struct ConversationConfig {
|
|
group: String,
|
|
group: String,
|
|
- server: String,
|
|
+ message_server: String,
|
|
|
|
+ web_server: String,
|
|
bootstrap: f64,
|
|
bootstrap: f64,
|
|
retry: f64,
|
|
retry: f64,
|
|
distributions: ConfigDistributions,
|
|
distributions: ConfigDistributions,
|