/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::{env, fs};
use async_std::sync::{Arc, Mutex};
use log::{error, info};
use smol::Executor;
use url::Url;
use super::{
transport::{TcpTransport, TorTransport, Transport, TransportListener, TransportName},
Channel, ChannelPtr, SessionWeakPtr,
};
use crate::{
system::{StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr, Subscription},
Error, Result,
};
/// Atomic pointer to Acceptor class.
pub type AcceptorPtr = Arc;
/// Create inbound socket connections.
pub struct Acceptor {
channel_subscriber: SubscriberPtr>,
task: StoppableTaskPtr,
pub session: Mutex>,
}
impl Acceptor {
/// Create new Acceptor object.
pub fn new(session: Mutex >) -> Arc {
Arc::new(Self {
channel_subscriber: Subscriber::new(),
task: StoppableTask::new(),
session,
})
}
/// Start accepting inbound socket connections. Creates a listener to start
/// listening on a local socket address. Then runs an accept loop in a new
/// thread, erroring if a connection problem occurs.
pub async fn start(
self: Arc,
accept_url: Url,
executor: Arc>,
) -> Result<()> {
let transport_name = TransportName::try_from(accept_url.clone())?;
macro_rules! accept {
($listener:expr, $transport:expr, $upgrade:expr) => {{
if let Err(err) = $listener {
error!(target: "net::acceptor", "Setup for {} failed: {}", accept_url, err);
return Err(Error::BindFailed(accept_url.as_str().into()))
}
let listener = $listener?.await;
if let Err(err) = listener {
error!(target: "net::acceptor", "Bind listener to {} failed: {}", accept_url, err);
return Err(Error::BindFailed(accept_url.as_str().into()))
}
let listener = listener?;
match $upgrade {
None => {
self.accept(Box::new(listener), executor);
}
Some(u) if u == "tls" => {
let tls_listener = $transport.upgrade_listener(listener)?.await?;
self.accept(Box::new(tls_listener), executor);
}
Some(u) => return Err(Error::UnsupportedTransportUpgrade(u)),
}
}};
}
match transport_name {
TransportName::Tcp(upgrade) => {
let transport = TcpTransport::new(None, 1024);
let listener = transport.listen_on(accept_url.clone());
accept!(listener, transport, upgrade);
}
TransportName::Tor(upgrade) => {
let socks5_url = Url::parse(
&env::var("DARKFI_TOR_SOCKS5_URL")
.unwrap_or_else(|_| "socks5://127.0.0.1:9050".to_string()),
)?;
let torc_url = Url::parse(
&env::var("DARKFI_TOR_CONTROL_URL")
.unwrap_or_else(|_| "tcp://127.0.0.1:9051".to_string()),
)?;
let auth_cookie = env::var("DARKFI_TOR_COOKIE");
if auth_cookie.is_err() {
return Err(Error::TorError(
"Please set the env var DARKFI_TOR_COOKIE to the configured tor cookie file. \
For example: \
\'export DARKFI_TOR_COOKIE=\"/var/lib/tor/control_auth_cookie\"\'".to_string(),
));
}
let auth_cookie = auth_cookie.unwrap();
let auth_cookie = hex::encode(fs::read(auth_cookie).unwrap());
let transport = TorTransport::new(socks5_url, Some((torc_url, auth_cookie)))?;
// generate EHS pointing to local address
let hurl = transport.create_ehs(accept_url.clone())?;
info!(target: "net::acceptor", "EHS TOR: {}", hurl.to_string());
let listener = transport.clone().listen_on(accept_url.clone());
accept!(listener, transport, upgrade);
}
_ => unimplemented!(),
}
Ok(())
}
/// Stop accepting inbound socket connections.
pub async fn stop(&self) {
// Send stop signal
self.task.stop().await;
}
/// Start receiving network messages.
pub async fn subscribe(self: Arc) -> Subscription> {
self.channel_subscriber.clone().subscribe().await
}
/// Run the accept loop in a new thread and error if a connection problem
/// occurs.
fn accept(self: Arc, listener: Box, executor: Arc>) {
let self2 = self.clone();
self.task.clone().start(
self.clone().run_accept_loop(listener),
|result| self2.handle_stop(result),
Error::NetworkServiceStopped,
executor,
);
}
/// Run the accept loop.
async fn run_accept_loop(self: Arc, listener: Box) -> Result<()> {
loop {
match listener.next().await {
Ok((stream, url)) => {
let channel =
Channel::new(stream, url, self.session.lock().await.clone().unwrap()).await;
self.channel_subscriber.notify(Ok(channel)).await;
}
Err(e) => {
error!(target: "net::acceptor", "Error listening for new connection: {}", e);
}
}
}
}
/// Handles network errors. Panics if error passes silently, otherwise
/// broadcasts the error.
async fn handle_stop(self: Arc, result: Result<()>) {
match result {
Ok(()) => panic!("Acceptor task should never complete without error status"),
Err(err) => {
// Send this error to all channel subscribers
let result = Err(err);
self.channel_subscriber.notify(result).await;
}
}
}
}