diff --git a/Cargo.lock b/Cargo.lock index b6a6a64fc5..c0951fe7d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4613,6 +4613,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "mixtcp" +version = "0.0.1" +dependencies = [ + "bytes", + "dirs 5.0.1", + "nym-bin-common 0.6.0", + "nym-ip-packet-requests", + "nym-sdk", + "reqwest 0.12.24", + "rustls 0.23.32", + "serde_json", + "smoltcp", + "thiserror 2.0.17", + "tokio", + "tracing", + "tracing-subscriber", + "webpki-roots 1.0.3", +] + [[package]] name = "mock_instant" version = "0.6.0" @@ -10620,26 +10640,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" -[[package]] -name = "smolmix" -version = "0.0.1" -dependencies = [ - "bytes", - "dirs 5.0.1", - "nym-bin-common 0.6.0", - "nym-ip-packet-requests", - "nym-sdk", - "reqwest 0.12.24", - "rustls 0.23.32", - "serde_json", - "smoltcp", - "thiserror 2.0.17", - "tokio", - "tracing", - "tracing-subscriber", - "webpki-roots 1.0.3", -] - [[package]] name = "smoltcp" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index 4d1d28d5de..a7c72700f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,7 +147,7 @@ members = [ "sdk/ffi/go", "sdk/ffi/shared", "sdk/rust/nym-sdk", - "smolmix", + "mixtcp", # "service-providers/authenticator", "service-providers/common", "service-providers/ip-packet-router", diff --git a/smolmix/Cargo.toml b/mixtcp/Cargo.toml similarity index 97% rename from smolmix/Cargo.toml rename to mixtcp/Cargo.toml index 2b3acb4e38..6bfd895b9c 100644 --- a/smolmix/Cargo.toml +++ b/mixtcp/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "smolmix" +name = "mixtcp" version = "0.0.1" edition = "2021" diff --git a/mixtcp/README.md b/mixtcp/README.md new file mode 100644 index 0000000000..d77963de1f --- /dev/null +++ b/mixtcp/README.md @@ -0,0 +1,3 @@ +# mixtcp poc + +Test TLS conn + mixtcp device HTTPS req/res via IPRs with `cargo test`. More to come. diff --git a/smolmix/examples/cloudflare_ping.rs b/mixtcp/examples/cloudflare_ping.rs similarity index 94% rename from smolmix/examples/cloudflare_ping.rs rename to mixtcp/examples/cloudflare_ping.rs index a8d08c6e0f..39e7269baf 100644 --- a/smolmix/examples/cloudflare_ping.rs +++ b/mixtcp/examples/cloudflare_ping.rs @@ -1,5 +1,5 @@ +use mixtcp::{create_device, MixtcpError}; use rustls::{pki_types::ServerName, ClientConfig, ClientConnection}; -use smolmix::{create_device, SmolmixError}; use std::{ io::{self, Read, Write}, sync::Arc, @@ -23,7 +23,7 @@ pub struct TlsOverTcp { } impl TlsOverTcp { - pub fn new(domain: &str) -> Result { + pub fn new(domain: &str) -> Result { let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); @@ -32,24 +32,24 @@ impl TlsOverTcp { .with_no_client_auth(); let server_name = ServerName::try_from(domain) - .map_err(|_| SmolmixError::InvalidDnsName)? + .map_err(|_| MixtcpError::InvalidDnsName)? .to_owned(); let conn = ClientConnection::new(Arc::new(config), server_name) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; Ok(Self { conn }) } /// Move data from TLS connection to TCP socket - pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { let mut buf = [0u8; 4096]; while self.conn.wants_write() { match self.conn.write_tls(&mut buf.as_mut_slice()) { Ok(n) if n > 0 => { socket .send_slice(&buf[..n]) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; } _ => break, } @@ -58,7 +58,7 @@ impl TlsOverTcp { } /// Move data from TCP socket to TLS connection - pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { if socket.can_recv() { let _ = socket.recv(|chunk| { if !chunk.is_empty() { @@ -72,15 +72,15 @@ impl TlsOverTcp { Ok(()) } - pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), MixtcpError> { self.conn .writer() .write_all(data) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; self.write_tls(socket) } - pub fn recv(&mut self, socket: &mut tcp::Socket) -> Result, SmolmixError> { + pub fn recv(&mut self, socket: &mut tcp::Socket) -> Result, MixtcpError> { self.read_tls(socket)?; let mut result = Vec::new(); let mut buf = vec![0u8; 4096]; @@ -223,7 +223,7 @@ async fn main() -> Result<(), Box> { info!("TLS handshake completed - ready for HTTPS"); // Send simple HTTP request - let request = b"GET /cdn-cgi/trace HTTP/1.1\r\nHost: cloudflare.com\r\nUser-Agent: smolmix-test/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n"; + let request = b"GET /cdn-cgi/trace HTTP/1.1\r\nHost: cloudflare.com\r\nUser-Agent: mixtcp-test/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n"; match tls_conn.send(request, socket) { Ok(_) => { info!("HTTPS request sent"); diff --git a/smolmix/examples/https_client.rs b/mixtcp/examples/https_client.rs similarity index 86% rename from smolmix/examples/https_client.rs rename to mixtcp/examples/https_client.rs index 5e8dcbb5c5..fa85431b83 100644 --- a/smolmix/examples/https_client.rs +++ b/mixtcp/examples/https_client.rs @@ -1,7 +1,7 @@ +use mixtcp::{create_device, MixtcpError, NymIprDevice}; use nym_sdk::stream_wrapper::IpMixStream; use reqwest::StatusCode; use rustls::{pki_types::ServerName, ClientConfig, ClientConnection}; -use smolmix::{create_device, NymIprDevice, SmolmixError}; use smoltcp::{ iface::{Config, Interface, SocketSet}, socket::tcp, @@ -23,7 +23,7 @@ pub struct TlsOverTcp { } impl TlsOverTcp { - pub fn new(domain: &str) -> Result { + pub fn new(domain: &str) -> Result { let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); @@ -32,23 +32,23 @@ impl TlsOverTcp { .with_no_client_auth(); let server_name = ServerName::try_from(domain) - .map_err(|_| SmolmixError::InvalidDnsName)? + .map_err(|_| MixtcpError::InvalidDnsName)? .to_owned(); let conn = ClientConnection::new(Arc::new(config), server_name) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; Ok(Self { conn }) } - pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { let mut buf = [0u8; 4096]; while self.conn.wants_write() { match self.conn.write_tls(&mut buf.as_mut_slice()) { Ok(n) if n > 0 => { socket .send_slice(&buf[..n]) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; } _ => break, } @@ -56,7 +56,7 @@ impl TlsOverTcp { Ok(()) } - pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { if socket.can_recv() { let _ = socket.recv(|chunk| { if !chunk.is_empty() { @@ -69,27 +69,27 @@ impl TlsOverTcp { Ok(()) } - pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), MixtcpError> { self.conn .writer() .write_all(data) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; self.write_tls(socket) } } /// Reqwest-ish client right now, just a handrolled GET request for the example -pub struct SmolmixReqwestClient { +pub struct MixtcpReqwestClient { device: Arc>, _bridge: tokio::task::JoinHandle<()>, _allocated_ip: Ipv4Address, } -impl SmolmixReqwestClient { - pub async fn new() -> Result { +impl MixtcpReqwestClient { + pub async fn new() -> Result { let ipr_stream = IpMixStream::new() .await - .map_err(|_| SmolmixError::MixnetConnectionFailed)?; + .map_err(|_| MixtcpError::MixnetConnectionFailed)?; let (mut device, bridge, allocated_ips) = create_device(ipr_stream).await?; info!("Allocated IP: {}", allocated_ips.ipv4); @@ -121,18 +121,18 @@ impl SmolmixReqwestClient { }) } - pub async fn get(&self, url: &str) -> Result { - let parsed_url = reqwest::Url::parse(url).map_err(|_| SmolmixError::InvalidUrl)?; - let host = parsed_url.host_str().ok_or(SmolmixError::InvalidUrl)?; + pub async fn get(&self, url: &str) -> Result { + let parsed_url = reqwest::Url::parse(url).map_err(|_| MixtcpError::InvalidUrl)?; + let host = parsed_url.host_str().ok_or(MixtcpError::InvalidUrl)?; let path = parsed_url.path(); let response_bytes = self.simple_get_request(host, path).await?; let (status, body) = self.parse_simple_response(&response_bytes)?; - Ok(SmolmixResponse { status, body }) + Ok(MixtcpResponse { status, body }) } - async fn simple_get_request(&self, domain: &str, path: &str) -> Result, SmolmixError> { + async fn simple_get_request(&self, domain: &str, path: &str) -> Result, MixtcpError> { let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 16384]); let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); @@ -155,7 +155,7 @@ impl SmolmixReqwestClient { loop { if start.elapsed() > Duration::from_secs(60) { - return Err(SmolmixError::Timeout); + return Err(MixtcpError::Timeout); } iface.poll(timestamp, device, &mut sockets); @@ -170,7 +170,7 @@ impl SmolmixReqwestClient { } Err(e) => { info!("TCP connect failed: {}", e); - return Err(SmolmixError::TcpConnectionFailed); + return Err(MixtcpError::TcpConnectionFailed); } } } @@ -181,7 +181,7 @@ impl SmolmixReqwestClient { Ok(t) => tls = Some(t), Err(e) => { info!("TLS create failed: {}", e); - return Err(SmolmixError::TlsHandshakeFailed); + return Err(MixtcpError::TlsHandshakeFailed); } } } @@ -195,7 +195,7 @@ impl SmolmixReqwestClient { info!("TLS handshake completed - ready for HTTPS"); let request = format!( - "GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: smolmix/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n", + "GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: mixtcp/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n", path, domain ); tls_conn.send(request.as_bytes(), socket)?; @@ -221,7 +221,7 @@ impl SmolmixReqwestClient { Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {} Err(e) => { info!("Read error: {}", e); - return Err(SmolmixError::ResponseReadFailed); + return Err(MixtcpError::ResponseReadFailed); } Ok(_) => continue, } @@ -230,17 +230,17 @@ impl SmolmixReqwestClient { tokio::time::sleep(Duration::from_millis(10)).await; } - Err(SmolmixError::NoResponseReceived) + Err(MixtcpError::NoResponseReceived) } /// Simple response - just extract status and body - fn parse_simple_response(&self, response_bytes: &[u8]) -> Result<(u16, String), SmolmixError> { + fn parse_simple_response(&self, response_bytes: &[u8]) -> Result<(u16, String), MixtcpError> { let response_str = String::from_utf8_lossy(response_bytes); let status_line = response_str .lines() .next() - .ok_or(SmolmixError::InvalidHttpResponse)?; + .ok_or(MixtcpError::InvalidHttpResponse)?; let status: u16 = status_line .split_whitespace() @@ -252,17 +252,17 @@ impl SmolmixReqwestClient { let body = response_str[body_start + 4..].to_string(); Ok((status, body)) } else { - Err(SmolmixError::InvalidHttpResponse) + Err(MixtcpError::InvalidHttpResponse) } } } -pub struct SmolmixResponse { +pub struct MixtcpResponse { status: u16, body: String, } -impl SmolmixResponse { +impl MixtcpResponse { pub fn status(&self) -> StatusCode { StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) } @@ -300,7 +300,7 @@ async fn main() -> Result<(), Box> { ); info!("Setting up mixnet client..."); - let client = SmolmixReqwestClient::new().await?; + let client = MixtcpReqwestClient::new().await?; let start = tokio::time::Instant::now(); let mixnet_response = client.get(test_url).await?; let mixnet_status = mixnet_response.status(); diff --git a/smolmix/examples/tls.rs b/mixtcp/examples/tls.rs similarity index 94% rename from smolmix/examples/tls.rs rename to mixtcp/examples/tls.rs index 9dcc70d3fd..7f2a9edae1 100644 --- a/smolmix/examples/tls.rs +++ b/mixtcp/examples/tls.rs @@ -1,5 +1,5 @@ +use mixtcp::{create_device, MixtcpError}; use rustls::{pki_types::ServerName, ClientConfig, ClientConnection}; -use smolmix::{create_device, SmolmixError}; use std::{ io::{self, Read, Write}, sync::Arc, @@ -23,7 +23,7 @@ pub struct TlsOverTcp { } impl TlsOverTcp { - pub fn new(domain: &str) -> Result { + pub fn new(domain: &str) -> Result { let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); @@ -32,24 +32,24 @@ impl TlsOverTcp { .with_no_client_auth(); let server_name = ServerName::try_from(domain) - .map_err(|_| SmolmixError::InvalidDnsName)? + .map_err(|_| MixtcpError::InvalidDnsName)? .to_owned(); let conn = ClientConnection::new(Arc::new(config), server_name) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; Ok(Self { conn }) } /// Move data from TLS connection to TCP socket - pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn write_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { let mut buf = [0u8; 4096]; while self.conn.wants_write() { match self.conn.write_tls(&mut buf.as_mut_slice()) { Ok(n) if n > 0 => { socket .send_slice(&buf[..n]) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; } _ => break, } @@ -58,7 +58,7 @@ impl TlsOverTcp { } /// Move data from TCP socket to TLS connection - pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn read_tls(&mut self, socket: &mut tcp::Socket) -> Result<(), MixtcpError> { if socket.can_recv() { let _ = socket.recv(|chunk| { if !chunk.is_empty() { @@ -72,15 +72,15 @@ impl TlsOverTcp { Ok(()) } - pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), SmolmixError> { + pub fn send(&mut self, data: &[u8], socket: &mut tcp::Socket) -> Result<(), MixtcpError> { self.conn .writer() .write_all(data) - .map_err(|_| SmolmixError::TlsHandshakeFailed)?; + .map_err(|_| MixtcpError::TlsHandshakeFailed)?; self.write_tls(socket) } - pub fn recv(&mut self, socket: &mut tcp::Socket) -> Result, SmolmixError> { + pub fn recv(&mut self, socket: &mut tcp::Socket) -> Result, MixtcpError> { self.read_tls(socket)?; let mut result = Vec::new(); let mut buf = vec![0u8; 4096]; diff --git a/smolmix/src/bridge.rs b/mixtcp/src/bridge.rs similarity index 96% rename from smolmix/src/bridge.rs rename to mixtcp/src/bridge.rs index f2122eef24..a82b77e911 100644 --- a/smolmix/src/bridge.rs +++ b/mixtcp/src/bridge.rs @@ -1,7 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-2.0-only -use crate::error::SmolmixError; +use crate::error::MixtcpError; use nym_ip_packet_requests::codec::MultiIpPacketCodec; use nym_sdk::stream_wrapper::IpMixStream; use tokio::sync::mpsc; @@ -55,7 +55,7 @@ impl NymIprBridge { /// - Maintains packet statistics /// /// The loop exits when channels are closed or an error occurs. - pub async fn run(mut self) -> Result<(), SmolmixError> { + pub async fn run(mut self) -> Result<(), MixtcpError> { info!("Starting Nym IPR bridge"); let mut packets_sent = 0; let mut packets_received = 0; @@ -100,7 +100,7 @@ impl NymIprBridge { // Forward to device via channel if let Err(_) = self.rx_sender.send(packet.to_vec()) { error!("Failed to send packet to device - receiver dropped"); - return Err(SmolmixError::ChannelClosed); + return Err(MixtcpError::ChannelClosed); } packets_received += 1; info!("Total packets received: {}", packets_received); diff --git a/smolmix/src/device.rs b/mixtcp/src/device.rs similarity index 100% rename from smolmix/src/device.rs rename to mixtcp/src/device.rs diff --git a/smolmix/src/error.rs b/mixtcp/src/error.rs similarity index 97% rename from smolmix/src/error.rs rename to mixtcp/src/error.rs index cf6210df40..56d80807d1 100644 --- a/smolmix/src/error.rs +++ b/mixtcp/src/error.rs @@ -4,7 +4,7 @@ use thiserror::Error; #[derive(Error, Debug)] -pub enum SmolmixError { +pub enum MixtcpError { #[error("Channel closed")] ChannelClosed, diff --git a/smolmix/src/lib.rs b/mixtcp/src/lib.rs similarity index 91% rename from smolmix/src/lib.rs rename to mixtcp/src/lib.rs index 786ad8a796..76abe07fc6 100644 --- a/smolmix/src/lib.rs +++ b/mixtcp/src/lib.rs @@ -7,7 +7,7 @@ mod error; pub use bridge::NymIprBridge; pub use device::NymIprDevice; -pub use error::SmolmixError; +pub use error::MixtcpError; use nym_ip_packet_requests::IpPair; use nym_sdk::stream_wrapper::IpMixStream; @@ -23,7 +23,7 @@ use tokio::sync::mpsc; /// - Constructs the device and bridge components pub async fn create_device( mut ipr_stream: IpMixStream, -) -> Result<(NymIprDevice, NymIprBridge, IpPair), SmolmixError> { +) -> Result<(NymIprDevice, NymIprBridge, IpPair), MixtcpError> { // Ensure the stream is connected if !ipr_stream.is_connected() { ipr_stream.connect_tunnel().await?; @@ -33,7 +33,7 @@ pub async fn create_device( // further 'up' the flow in the code calling this fn (see examples/tcp_connect.rs). let allocated_ips = ipr_stream .allocated_ips() - .ok_or(SmolmixError::NotConnected)? + .ok_or(MixtcpError::NotConnected)? .clone(); // Create channels for device <-> bridge communication diff --git a/smolmix/README.md b/smolmix/README.md deleted file mode 100644 index ed5b5f1a40..0000000000 --- a/smolmix/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# smolmix poc - -Test TLS conn + smolmix device HTTPS req/res via IPRs with `cargo test`. More to come.