Tweak comments

This commit is contained in:
mfahampshire
2026-04-30 21:55:45 +01:00
parent 3df7691e33
commit f0c3fc00a6
10 changed files with 73 additions and 33 deletions
-1
View File
@@ -1,5 +1,4 @@
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
use crate::error::SmolmixError;
use futures::channel::mpsc;
+10
View File
@@ -4,15 +4,25 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum SmolmixError {
/// The internal channel between the bridge and the network device was closed.
///
/// This typically means the bridge task has exited — either because the tunnel
/// was shut down or the mixnet connection was lost.
#[error("Channel closed")]
ChannelClosed,
/// The IPR handshake has not completed.
///
/// Returned from [`Tunnel::from_stream`](crate::Tunnel::from_stream) when
/// the provided `IpMixStream` is not in a connected state.
#[error("Not connected to IPR")]
NotConnected,
/// An error from the Nym SDK (mixnet client, gateway connection, etc.).
#[error("Nym SDK error: {0}")]
NymSdk(#[from] nym_sdk::Error),
/// An I/O error from the underlying TCP/UDP socket operations.
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
+3 -5
View File
@@ -1,5 +1,8 @@
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
//! TCP/UDP tunnel over the Nym mixnet — standard `TcpStream` and `UdpSocket`
//! types that route all traffic through a mixnet for metadata privacy.
#![doc = include_str!("ARCHITECTURE.md")]
mod bridge;
@@ -35,12 +38,7 @@ pub use tunnel::Recipient;
/// ```
pub use tunnel::TcpStream;
/// A mixnet tunnel providing TCP and UDP socket access.
pub use tunnel::Tunnel;
/// Builder for configuring and creating a [`Tunnel`].
///
/// See [`Tunnel::builder()`] for usage.
pub use tunnel::TunnelBuilder;
/// A UDP socket routed through the mixnet. Supports `send_to` / `recv_from`.
-4
View File
@@ -48,7 +48,6 @@ struct TunnelInner {
/// (Internet Packet Router). It spawns a background bridge task and a network reactor,
/// then provides familiar socket APIs on top.
///
///
/// # Shutdown
///
/// Call [`shutdown()`](Self::shutdown) for a clean disconnect. Rust has no async `Drop`,
@@ -115,9 +114,6 @@ pub struct Tunnel {
/// For full control over the mixnet client (credentials, gateway selection,
/// storage, etc.), configure an [`IpMixStream`] directly and pass it to
/// [`Tunnel::from_stream()`].
///
/// **Note:** deeper builder integration with `MixnetClientBuilder` requires
/// upstream SDK changes to expose `IpMixStream` internals.
pub struct TunnelBuilder {
ipr_address: Option<Recipient>,
}
+38 -7
View File
@@ -1,5 +1,4 @@
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
//! DNS resolution through the Nym mixnet.
//!
@@ -13,10 +12,10 @@
//!
//! # How it works
//!
//! [hickory-resolver]'s extension point is the [`RuntimeProvider`] trait — it
//! controls how the resolver creates TCP connections and UDP sockets.
//! [`SmolmixRuntimeProvider`] implements this trait, routing all I/O through
//! the tunnel:
//! [hickory-resolver](https://docs.rs/hickory-resolver)'s extension point is
//! the [`RuntimeProvider`] trait — it controls how the resolver creates TCP
//! connections and UDP sockets. [`SmolmixRuntimeProvider`] implements this
//! trait, routing all I/O through the tunnel:
//!
//! ```text
//! RuntimeProvider::connect_tcp() → tunnel.tcp_connect() → AsyncIoTokioAsStd<TcpStream>
@@ -28,7 +27,7 @@
//! hickory's `DnsTcpStream` has a blanket impl for any `futures_io::AsyncRead +
//! AsyncWrite`, the wrapped stream satisfies it automatically.
//!
//! For UDP, [`SmolmixUdpSocket`] is a thin newtype over `tokio_smoltcp::UdpSocket`
//! For UDP, `SmolmixUdpSocket` is a thin newtype over `tokio_smoltcp::UdpSocket`
//! that implements hickory's [`DnsUdpSocket`](hickory_proto::udp::DnsUdpSocket)
//! — just delegates `poll_recv_from` and `poll_send_to`.
//!
@@ -68,7 +67,7 @@
//! use smolmix_dns::{Resolver, ResolverConfig};
//!
//! let tunnel = smolmix::Tunnel::new().await?;
//! let resolver = Resolver::with_config(&tunnel, ResolverConfig::quad9());
//! let resolver = Resolver::with_config(&tunnel, ResolverConfig::cloudflare());
//! # Ok(())
//! # }
//! ```
@@ -81,6 +80,7 @@ use std::io;
use std::net::SocketAddr;
use std::ops::Deref;
use hickory_resolver::config::NameServerConfigGroup;
use hickory_resolver::name_server::GenericConnector;
use hickory_proto::runtime::TokioHandle;
@@ -122,7 +122,17 @@ impl Resolver {
}
/// Create a resolver with a custom upstream DNS configuration.
///
/// IPv6 nameservers are filtered out because the tunnel's smoltcp
/// interface is IPv4-only (see [`tokio_smoltcp::NetConfig`]). Passing a
/// config with *only* IPv6 nameservers will cause lookups to fail.
pub fn with_config(tunnel: &Tunnel, config: ResolverConfig) -> Self {
// tokio-smoltcp only supports a single IpCidr (IPv4), so contacting
// an IPv6 nameserver panics in smoltcp's wire layer (IP version
// mismatch). Strip IPv6 nameservers until the tunnel supports
// dual-stack.
let config = ipv4_only_nameservers(config);
let provider = SmolmixRuntimeProvider {
tunnel: tunnel.clone(),
handle: TokioHandle::default(),
@@ -172,3 +182,24 @@ pub async fn resolve(tunnel: &Tunnel, host: &str, port: u16) -> io::Result<Vec<S
let r = resolver(tunnel);
r.resolve(host, port).await
}
/// Strip IPv6 nameservers from a resolver config.
///
/// hickory's preset configs (quad9, cloudflare, google) all include IPv6
/// nameservers. The smolmix tunnel is IPv4-only (tokio-smoltcp's `NetConfig`
/// takes a single `IpCidr`), so sending a UDP packet to an IPv6 nameserver
/// panics in smoltcp's wire layer with "IP version mismatch".
fn ipv4_only_nameservers(config: ResolverConfig) -> ResolverConfig {
let ipv4_servers: Vec<_> = config
.name_servers()
.iter()
.filter(|ns| ns.socket_addr.is_ipv4())
.cloned()
.collect();
ResolverConfig::from_parts(
config.domain().cloned(),
config.search().to_vec(),
NameServerConfigGroup::from(ipv4_servers),
)
}
+1 -2
View File
@@ -1,5 +1,4 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
//! hickory-resolver [`RuntimeProvider`] routing all DNS I/O through a [`Tunnel`].
+16 -4
View File
@@ -1,11 +1,11 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
//! [`tower::Service<Uri>`] connector routing TCP + TLS through a [`Tunnel`].
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use hyper::Uri;
@@ -13,6 +13,7 @@ use hyper_util::rt::TokioIo;
use tower::Service;
use smolmix::Tunnel;
use smolmix_dns::Resolver;
use smolmix_tls::TlsConnector;
use crate::tls_stream::MaybeTlsStream;
@@ -21,20 +22,30 @@ use crate::tls_stream::MaybeTlsStream;
///
/// Implements [`tower::Service<Uri>`] so it plugs directly into hyper-util's `Client`.
/// DNS resolution also goes through the tunnel, preventing hostname leaks.
///
/// DNS lookups are cached internally via a shared [`smolmix_dns::Resolver`] —
/// repeat requests to the same host reuse cached records (subject to TTL).
///
/// Use this directly when you need a custom body type (e.g. `Full<Bytes>` for
/// POST requests) — see the [crate-level docs](crate#sending-request-bodies-post-put-etc)
/// for an example.
#[derive(Clone)]
pub struct SmolmixConnector {
tunnel: Tunnel,
tls: TlsConnector,
resolver: Arc<Resolver>,
}
impl SmolmixConnector {
/// Create a new connector for the given tunnel.
///
/// Sets up a TLS connector with the standard webpki root certificates.
/// Sets up a TLS connector with the standard webpki root certificates and
/// a DNS resolver that caches lookups across requests.
pub fn new(tunnel: &Tunnel) -> Self {
Self {
tunnel: tunnel.clone(),
tls: smolmix_tls::connector(),
resolver: Arc::new(Resolver::new(tunnel)),
}
}
}
@@ -51,6 +62,7 @@ impl Service<Uri> for SmolmixConnector {
fn call(&mut self, uri: Uri) -> Self::Future {
let tunnel = self.tunnel.clone();
let tls = self.tls.clone();
let resolver = self.resolver.clone();
Box::pin(async move {
let scheme = uri.scheme_str().unwrap_or("https");
@@ -62,7 +74,7 @@ impl Service<Uri> for SmolmixConnector {
.port_u16()
.unwrap_or(if scheme == "https" { 443 } else { 80 });
let addrs = smolmix_dns::resolve(&tunnel, &host, port).await?;
let addrs = resolver.resolve(&host, port).await?;
let addr = addrs
.into_iter()
.next()
+1 -7
View File
@@ -1,5 +1,4 @@
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
//! HTTP client routing all traffic through the Nym mixnet.
//!
@@ -19,7 +18,7 @@
//!
//! ```text
//! SmolmixConnector::call(uri)
//! → smolmix_dns::resolve(host, port) DNS through tunnel
//! → resolver.resolve(host, port) DNS through tunnel (cached)
//! → tunnel.tcp_connect(addr) TCP through mixnet
//! → smolmix_tls::connect_with(tls, tcp, host) TLS if https
//! → MaybeTlsStream::Plain { TcpStream }
@@ -116,12 +115,7 @@ pub use http_body_util::Empty as EmptyBody;
/// Re-exported hyper types for building requests without depending on hyper directly.
pub use hyper::{Request, Response, StatusCode, Uri};
/// The connector implementing `tower::Service<Uri>`. Use this directly when you
/// need a custom body type (e.g. `Full<Bytes>` for POST requests).
pub use connector::SmolmixConnector;
/// A stream that may or may not be wrapped in TLS. Returned by [`SmolmixConnector`]
/// — you won't normally interact with this directly.
pub use tls_stream::MaybeTlsStream;
/// Inner hyper-util client type alias for readability.
+4 -2
View File
@@ -1,5 +1,4 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
//! TLS stream abstraction for plain and encrypted connections.
@@ -13,6 +12,9 @@ use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pin_project! {
/// A stream that may or may not be wrapped in TLS.
///
/// Returned by [`SmolmixConnector`](crate::SmolmixConnector) — you won't
/// normally interact with this directly.
#[project = MaybeTlsProj]
pub enum MaybeTlsStream {
Plain { #[pin] inner: tokio_smoltcp::TcpStream },
-1
View File
@@ -1,5 +1,4 @@
// Copyright 2024-2026 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-2.0-only
//! Shared TLS configuration for smolmix tunneled connections.
//!