Feature/sphinx socket packet encoder (#245)
* Ability to send sphinx packets of different sizes + more efficient decoding * Closing connection on connection corruption * Missing semicolons * Missing license notices * Default for packetsize
This commit is contained in:
committed by
GitHub
parent
aeb1a4b22e
commit
860a69b246
@@ -15,29 +15,32 @@
|
||||
use crate::node::packet_processing::{MixProcessingResult, PacketProcessor};
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nymsphinx::framing::SphinxCodec;
|
||||
use nymsphinx::SphinxPacket;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::prelude::*;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
async fn process_received_packet(
|
||||
packet_data: [u8; nymsphinx::PACKET_SIZE],
|
||||
sphinx_packet: SphinxPacket,
|
||||
packet_processor: PacketProcessor,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec<u8>)>,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, SphinxPacket)>,
|
||||
) {
|
||||
// all processing incl. delay was done, the only thing left is to forward it
|
||||
match packet_processor.process_sphinx_packet(packet_data).await {
|
||||
match packet_processor.process_sphinx_packet(sphinx_packet).await {
|
||||
Err(e) => debug!("We failed to process received sphinx packet - {:?}", e),
|
||||
Ok(res) => match res {
|
||||
MixProcessingResult::ForwardHop(hop_address, hop_data) => {
|
||||
MixProcessingResult::ForwardHop(hop_address, forward_packet) => {
|
||||
// send our data to tcp client for forwarding. If forwarding fails, then it fails,
|
||||
// it's not like we can do anything about it
|
||||
//
|
||||
// in unbounded_send() failed it means that the receiver channel was disconnected
|
||||
// and hence something weird must have happened without a way of recovering
|
||||
forwarding_channel
|
||||
.unbounded_send((hop_address, hop_data))
|
||||
.unbounded_send((hop_address, forward_packet))
|
||||
.unwrap();
|
||||
packet_processor.report_sent(hop_address);
|
||||
}
|
||||
@@ -49,57 +52,44 @@ async fn process_received_packet(
|
||||
}
|
||||
|
||||
async fn process_socket_connection(
|
||||
mut socket: tokio::net::TcpStream,
|
||||
socket: tokio::net::TcpStream,
|
||||
packet_processor: PacketProcessor,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec<u8>)>,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, SphinxPacket)>,
|
||||
) {
|
||||
let mut buf = [0u8; nymsphinx::PACKET_SIZE];
|
||||
loop {
|
||||
match socket.read_exact(&mut buf).await {
|
||||
// socket closed
|
||||
Ok(n) if n == 0 => {
|
||||
trace!("Remote connection closed.");
|
||||
return;
|
||||
}
|
||||
Ok(n) => {
|
||||
// If I understand it correctly, this if should never be executed as if `read_exact`
|
||||
// does not fill buffer, it will throw UnexpectedEof?
|
||||
if n != nymsphinx::PACKET_SIZE {
|
||||
warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, nymsphinx::PACKET_SIZE);
|
||||
continue;
|
||||
}
|
||||
// we must be able to handle multiple packets from same connection independently
|
||||
let mut framed = Framed::new(socket, SphinxCodec);
|
||||
while let Some(sphinx_packet) = framed.next().await {
|
||||
match sphinx_packet {
|
||||
Ok(sphinx_packet) => {
|
||||
// we *really* need a worker pool here, because if we receive too many packets,
|
||||
// we will spawn too many tasks and starve CPU due to context switching.
|
||||
// (because presumably tokio has some concept of context switching in its
|
||||
// scheduler)
|
||||
tokio::spawn(process_received_packet(
|
||||
buf,
|
||||
// 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)
|
||||
sphinx_packet,
|
||||
packet_processor.clone(),
|
||||
forwarding_channel.clone(),
|
||||
))
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::UnexpectedEof {
|
||||
debug!("Read buffer was not fully filled. Most likely the client ({:?}) closed the connection.\
|
||||
Also closing the connection on this end.", socket.peer_addr())
|
||||
} else {
|
||||
warn!(
|
||||
"failed to read from socket (source: {:?}). Closing the connection; err = {:?}",
|
||||
socket.peer_addr(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"The socket connection got corrupted with error: {:?}. Closing the socket",
|
||||
err
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
info!(
|
||||
"Closing connection from {:?}",
|
||||
framed.into_inner().peer_addr()
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn run_socket_listener(
|
||||
handle: &Handle,
|
||||
addr: SocketAddr,
|
||||
packet_processor: PacketProcessor,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec<u8>)>,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, SphinxPacket)>,
|
||||
) -> JoinHandle<io::Result<()>> {
|
||||
let handle_clone = handle.clone();
|
||||
handle.spawn(async move {
|
||||
|
||||
@@ -18,6 +18,7 @@ use crypto::encryption;
|
||||
use directory_client::presence::Topology;
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nymsphinx::SphinxPacket;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::runtime::Runtime;
|
||||
use topology::NymTopology;
|
||||
@@ -71,7 +72,7 @@ impl MixNode {
|
||||
fn start_socket_listener(
|
||||
&self,
|
||||
metrics_reporter: metrics::MetricsReporter,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec<u8>)>,
|
||||
forwarding_channel: mpsc::UnboundedSender<(SocketAddr, SphinxPacket)>,
|
||||
) {
|
||||
info!("Starting socket listener...");
|
||||
// this is the only location where our private key is going to be copied
|
||||
@@ -87,7 +88,7 @@ impl MixNode {
|
||||
);
|
||||
}
|
||||
|
||||
fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, Vec<u8>)> {
|
||||
fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, SphinxPacket)> {
|
||||
info!("Starting packet forwarder...");
|
||||
self.runtime
|
||||
.enter(|| {
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nymsphinx::SphinxPacket;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
pub(crate) struct PacketForwarder {
|
||||
tcp_client: multi_tcp_client::Client,
|
||||
conn_tx: mpsc::UnboundedSender<(SocketAddr, Vec<u8>)>,
|
||||
conn_rx: mpsc::UnboundedReceiver<(SocketAddr, Vec<u8>)>,
|
||||
tcp_client: mixnet_client::Client,
|
||||
conn_tx: mpsc::UnboundedSender<(SocketAddr, SphinxPacket)>,
|
||||
conn_rx: mpsc::UnboundedReceiver<(SocketAddr, SphinxPacket)>,
|
||||
}
|
||||
|
||||
impl PacketForwarder {
|
||||
@@ -31,7 +32,7 @@ impl PacketForwarder {
|
||||
maximum_reconnection_backoff: Duration,
|
||||
initial_connection_timeout: Duration,
|
||||
) -> PacketForwarder {
|
||||
let tcp_client_config = multi_tcp_client::Config::new(
|
||||
let tcp_client_config = mixnet_client::Config::new(
|
||||
initial_reconnection_backoff,
|
||||
maximum_reconnection_backoff,
|
||||
initial_connection_timeout,
|
||||
@@ -40,13 +41,16 @@ impl PacketForwarder {
|
||||
let (conn_tx, conn_rx) = mpsc::unbounded();
|
||||
|
||||
PacketForwarder {
|
||||
tcp_client: multi_tcp_client::Client::new(tcp_client_config),
|
||||
tcp_client: mixnet_client::Client::new(tcp_client_config),
|
||||
conn_tx,
|
||||
conn_rx,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self, handle: &Handle) -> mpsc::UnboundedSender<(SocketAddr, Vec<u8>)> {
|
||||
pub(crate) fn start(
|
||||
mut self,
|
||||
handle: &Handle,
|
||||
) -> mpsc::UnboundedSender<(SocketAddr, SphinxPacket)> {
|
||||
// TODO: what to do with the lost JoinHandle?
|
||||
let sender_channel = self.conn_tx.clone();
|
||||
handle.spawn(async move {
|
||||
|
||||
@@ -32,7 +32,7 @@ pub enum MixProcessingError {
|
||||
}
|
||||
|
||||
pub enum MixProcessingResult {
|
||||
ForwardHop(SocketAddr, Vec<u8>),
|
||||
ForwardHop(SocketAddr, SphinxPacket),
|
||||
#[allow(dead_code)]
|
||||
LoopMessage,
|
||||
}
|
||||
@@ -87,21 +87,16 @@ impl PacketProcessor {
|
||||
// Delay packet for as long as required
|
||||
tokio::time::delay_for(delay.to_duration()).await;
|
||||
|
||||
Ok(MixProcessingResult::ForwardHop(
|
||||
next_hop_address,
|
||||
packet.to_bytes(),
|
||||
))
|
||||
Ok(MixProcessingResult::ForwardHop(next_hop_address, packet))
|
||||
}
|
||||
|
||||
pub(crate) async fn process_sphinx_packet(
|
||||
&self,
|
||||
raw_packet_data: [u8; nymsphinx::PACKET_SIZE],
|
||||
packet: SphinxPacket,
|
||||
) -> Result<MixProcessingResult, 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).await
|
||||
|
||||
Reference in New Issue
Block a user