Add Tunnel::new_with_ipr, re-export IpPair/Recipient, tidy examples

- Add Tunnel::new_with_ipr() for targeting a specific exit node
- Re-export IpPair and Recipient so users don't need direct deps
- Add DNS leak warning to WebSocket example
- Await hyper connection task in HTTPS example
This commit is contained in:
mfahampshire
2026-03-25 00:20:22 +00:00
parent b205744da5
commit 781d3ee8d0
4 changed files with 29 additions and 5 deletions
+5 -1
View File
@@ -59,7 +59,7 @@ async fn main() -> Result<(), BoxError> {
// Hand the TLS stream to hyper for proper HTTP/1.1 handling.
let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(tls)).await?;
tokio::spawn(conn);
let conn_handle = tokio::spawn(conn);
let req = Request::get(path)
.header("Host", host)
@@ -94,6 +94,10 @@ async fn main() -> Result<(), BoxError> {
let slowdown = mixnet_duration.as_millis() as f64 / clearnet_duration.as_millis().max(1) as f64;
info!(" Slowdown: {slowdown:.1}x");
// Drop the sender so hyper knows we're done, then wait for the connection task.
drop(sender);
let _ = conn_handle.await;
tunnel.shutdown().await;
Ok(())
}
+5 -2
View File
@@ -47,12 +47,15 @@ async fn main() -> Result<(), BoxError> {
.install_default()
.expect("Failed to install rustls crypto provider");
// Resolve hostname via clearnet DNS (smolmix-dns will handle this later).
// WARNING: This resolves the hostname via clearnet DNS, leaking the target
// to a local observer. In production, resolve DNS through the tunnel itself
// (see tunnel_dns.rs) or use smolmix-dns when available. We use clearnet
// here only because this example needs a known IP before the tunnel is up.
let addr = tokio::net::lookup_host(format!("{WS_HOST}:443"))
.await?
.next()
.ok_or("DNS resolution failed")?;
info!("Resolved {WS_HOST} -> {addr}");
info!("Resolved {WS_HOST} -> {addr} (via clearnet DNS)");
let connector = tls_connector();
let domain = ServerName::try_from(WS_HOST)?.to_owned();
+1 -1
View File
@@ -9,7 +9,7 @@ mod error;
mod tunnel;
pub use error::SmolmixError;
pub use tunnel::{NetworkEnvironment, TcpStream, Tunnel, UdpSocket};
pub use tunnel::{IpPair, NetworkEnvironment, Recipient, TcpStream, Tunnel, UdpSocket};
/// Initialise the default tracing/logging subscriber.
pub fn init_logging() {
+18 -1
View File
@@ -12,7 +12,7 @@
use std::net::SocketAddr;
use std::sync::Arc;
use nym_ip_packet_requests::IpPair;
pub use nym_ip_packet_requests::IpPair;
use nym_sdk::ipr_wrapper::IpMixStream;
use smoltcp::iface::Config;
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address};
@@ -27,6 +27,7 @@ use tokio_smoltcp::{Net, NetConfig};
// Re-export so users only need `use smolmix::*` — no direct dep on nym-sdk or tokio-smoltcp.
pub use nym_sdk::ipr_wrapper::NetworkEnvironment;
pub use nym_sdk::mixnet::Recipient;
pub use tokio_smoltcp::{TcpStream, UdpSocket};
struct ShutdownState {
@@ -69,6 +70,22 @@ impl Tunnel {
Self::from_stream(ipr_stream).await
}
/// Create a new tunnel connected to a specific IPR exit node.
///
/// Use this for testing against a known exit gateway, or when you want to
/// bypass automatic IPR discovery:
/// ```ignore
/// let ipr: Recipient = "gateway-address...".parse()?;
/// let tunnel = Tunnel::new_with_ipr(NetworkEnvironment::Mainnet, ipr).await?;
/// ```
pub async fn new_with_ipr(
env: NetworkEnvironment,
ipr_address: Recipient,
) -> Result<Self, SmolmixError> {
let ipr_stream = IpMixStream::new_with_ipr(env, ipr_address).await?;
Self::from_stream(ipr_stream).await
}
/// Create a tunnel from a pre-configured [`IpMixStream`].
///
/// Use this if you need to customize the mixnet client (e.g. custom gateway,