Tweak comments
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
@@ -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,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`].
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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,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.
|
||||
//!
|
||||
|
||||
Reference in New Issue
Block a user