diff --git a/mixnode/src/node/listener.rs b/mixnode/src/node/listener.rs new file mode 100644 index 0000000000..d0d8be2082 --- /dev/null +++ b/mixnode/src/node/listener.rs @@ -0,0 +1,87 @@ +use crate::node::packet_processing::PacketProcessor; +use log::*; +use std::io; +use std::net::SocketAddr; +use tokio::prelude::*; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; + +async fn process_received_packet( + packet_data: [u8; sphinx::PACKET_SIZE], + processing_data: PacketProcessor, +) { + // if we fail then we fail + processing_data.process_sphinx_packet(packet_data); + + // let fwd_data = match PacketProcessor::process_sphinx_data_packet( + // &packet_data, + // processing_data, + // ) + // .await + // { + // Ok(fwd_data) => fwd_data, + // Err(e) => { + // warn!("failed to process sphinx packet: {:?}", e); + // return; + // } + // }; + // PacketProcessor::wait_and_forward(fwd_data).await; +} + +async fn process_socket_connection( + mut socket: tokio::net::TcpStream, + packet_processor: PacketProcessor, +) { + let mut buf = [0u8; sphinx::PACKET_SIZE]; + loop { + match socket.read(&mut buf).await { + // socket closed + Ok(n) if n == 0 => { + trace!("Remote connection closed."); + return; + } + Ok(n) => { + if n != sphinx::PACKET_SIZE { + warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, sphinx::PACKET_SIZE); + continue; + } + + // we must be able to handle multiple packets from same connection independently + tokio::spawn(process_received_packet( + buf.clone(), + // note: processing_data is relatively cheap (and safe) to clone - + // it contains arc to private key and metrics reporter (which is just + // a single mpsc unbounded sender) + // TODO: channel to tcp client + packet_processor.clone(), + )) + } + Err(e) => { + warn!( + "failed to read from socket. Closing the connection; err = {:?}", + e + ); + return; + } + }; + } +} + +pub(crate) fn run_socket_listener( + handle: &Handle, + addr: SocketAddr, + packet_processor: PacketProcessor, +) -> JoinHandle> { + let handle_clone = handle.clone(); + handle.spawn(async move { + let mut listener = tokio::net::TcpListener::bind(addr).await?; + loop { + let (socket, _) = listener.accept().await?; + + let thread_packet_processor = packet_processor.clone(); + handle_clone.spawn(async move { + process_socket_connection(socket, thread_packet_processor).await; + }); + } + }) +} diff --git a/mixnode/src/node/packet_processing.rs b/mixnode/src/node/packet_processing.rs new file mode 100644 index 0000000000..4080af472a --- /dev/null +++ b/mixnode/src/node/packet_processing.rs @@ -0,0 +1,146 @@ +use crate::node::metrics; +use crypto::encryption; +use log::*; +use sphinx::header::delays::Delay as SphinxDelay; +use sphinx::route::NodeAddressBytes; +use sphinx::{ProcessedPacket, SphinxPacket}; +use std::ops::Deref; +use std::sync::Arc; + +#[derive(Debug)] +pub enum MixProcessingError { + SphinxRecoveryError, + ReceivedFinalHopError, + SphinxProcessingError, + InvalidHopAddress, +} + +impl From for MixProcessingError { + // for time being just have a single error instance for all possible results of sphinx::ProcessingError + fn from(_: sphinx::ProcessingError) -> Self { + use MixProcessingError::*; + + SphinxRecoveryError + } +} + +// +// struct ForwardingData { +// packet: SphinxPacket, +// delay: SphinxDelay, +// recipient: MixPeer, +// sent_metrics_tx: mpsc::Sender, +// } +// +// // TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data +// impl ForwardingData { +// fn new( +// packet: SphinxPacket, +// delay: SphinxDelay, +// recipient: MixPeer, +// sent_metrics_tx: mpsc::Sender, +// ) -> Self { +// ForwardingData { +// packet, +// delay, +// recipient, +// sent_metrics_tx, +// } +// } +// } + +// PacketProcessor contains all data required to correctly unwrap and forward sphinx packets +#[derive(Clone)] +pub struct PacketProcessor { + secret_key: Arc, + metrics_reporter: metrics::MetricsReporter, +} + +impl PacketProcessor { + pub(crate) fn new( + secret_key: encryption::PrivateKey, + metrics_reporter: metrics::MetricsReporter, + ) -> Self { + PacketProcessor { + secret_key: Arc::new(secret_key), + metrics_reporter, + } + } + + // async fn wait_and_forward(mut forwarding_data: ForwardingData) { + // let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value()); + // tokio::time::delay_for(delay_duration).await; + // + // if forwarding_data + // .sent_metrics_tx + // .send(forwarding_data.recipient.stringify()) + // .await + // .is_err() + // { + // error!("failed to send metrics data to the controller - the underlying thread probably died!"); + // std::process::exit(1); + // } + // + // trace!("RECIPIENT: {:?}", forwarding_data.recipient); + // match forwarding_data + // .recipient + // .send(forwarding_data.packet.to_bytes()) + // .await + // { + // Ok(()) => (), + // Err(e) => { + // warn!( + // "failed to write bytes to next mix peer. err = {:?}", + // e.to_string() + // ); + // } + // } + // } + + fn process_forward_hop( + &self, + packet: SphinxPacket, + forward_address: NodeAddressBytes, + delay: SphinxDelay, + ) -> Result<(), MixProcessingError> { + unimplemented!() + // let next_mix = match MixPeer::new(next_hop_address) { + // Ok(next_mix) => next_mix, + // Err(_) => return Err(MixProcessingError::InvalidHopAddress), + // }; + // + // let fwd_data = ForwardingData::new( + // next_packet, + // delay, + // next_mix, + // processing_data.sent_metrics_tx.clone(), + // ); + // Ok(fwd_data) + + // TODO: do forwarding here? + } + + pub(crate) fn process_sphinx_packet( + &self, + raw_packet_data: [u8; sphinx::PACKET_SIZE], + ) -> Result<(), MixProcessingError> { + // we received something resembling a sphinx packet, report it! + self.metrics_reporter.report_received(); + + let packet = SphinxPacket::from_bytes(&raw_packet_data)?; + + match packet.process(self.secret_key.deref().inner()) { + Ok(ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay)) => { + self.process_forward_hop(packet, address, delay) + } + Ok(ProcessedPacket::ProcessedPacketFinalHop(_, _, _)) => { + warn!("Received a loop cover message that we haven't implemented yet!"); + Err(MixProcessingError::ReceivedFinalHopError) + } + Err(e) => { + warn!("Failed to unwrap Sphinx packet: {:?}", e); + Err(MixProcessingError::SphinxProcessingError) + } + } + } +}