use std::net::SocketAddr;
use std::sync::Arc;
use futures::channel::mpsc;
pub use nym_ip_packet_requests::IpPair;
use nym_sdk::ipr_wrapper::IpMixStream;
use smoltcp::iface::Config;
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr, Ipv4Address};
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use tracing::info;
use crate::bridge::{BridgeShutdownHandle, NymIprBridge};
use crate::device::NymAsyncDevice;
use crate::SmolmixError;
use tokio_smoltcp::{Net, NetConfig};
pub use nym_sdk::mixnet::Recipient;
pub use tokio_smoltcp::{TcpStream, UdpSocket};
struct ShutdownState {
bridge_shutdown: BridgeShutdownHandle,
bridge_handle: JoinHandle<Result<(), SmolmixError>>,
}
struct TunnelInner {
net: Net,
allocated_ips: IpPair,
shutdown: Mutex<Option<ShutdownState>>,
}
#[derive(Clone)]
pub struct Tunnel {
inner: Arc<TunnelInner>,
}
pub struct TunnelBuilder {
ipr_address: Option<Recipient>,
}
impl TunnelBuilder {
pub fn ipr_address(mut self, addr: Recipient) -> Self {
self.ipr_address = Some(addr);
self
}
pub async fn build(self) -> Result<Tunnel, SmolmixError> {
let stream = match self.ipr_address {
Some(addr) => IpMixStream::new_with_ipr(addr).await?,
None => IpMixStream::new().await?,
};
Tunnel::from_stream(stream).await
}
}
impl Tunnel {
pub fn builder() -> TunnelBuilder {
TunnelBuilder { ipr_address: None }
}
pub async fn new() -> Result<Self, SmolmixError> {
Self::builder().build().await
}
pub async fn new_with_ipr(ipr_address: Recipient) -> Result<Self, SmolmixError> {
Self::builder().ipr_address(ipr_address).build().await
}
pub async fn from_stream(ipr_stream: IpMixStream) -> Result<Self, SmolmixError> {
ipr_stream
.check_connected()
.map_err(|_| SmolmixError::NotConnected)?;
let allocated_ips = *ipr_stream.allocated_ips();
let (outgoing_tx, outgoing_rx) = mpsc::unbounded();
let (incoming_tx, incoming_rx) = mpsc::unbounded();
let (bridge, bridge_shutdown) = NymIprBridge::new(ipr_stream, outgoing_rx, incoming_tx);
let bridge_handle = tokio::spawn(bridge.run());
let device = NymAsyncDevice::new(incoming_rx, outgoing_tx);
let iface_config = Config::new(HardwareAddress::Ip);
let net_config = NetConfig::new(
iface_config,
IpCidr::new(IpAddress::from(allocated_ips.ipv4), 32),
vec![IpAddress::from(Ipv4Address::UNSPECIFIED)],
);
let net = Net::new(device, net_config);
info!("Tunnel ready, allocated IP: {}", allocated_ips.ipv4);
Ok(Self {
inner: Arc::new(TunnelInner {
net,
allocated_ips,
shutdown: Mutex::new(Some(ShutdownState {
bridge_shutdown,
bridge_handle,
})),
}),
})
}
pub async fn tcp_connect(&self, addr: SocketAddr) -> Result<TcpStream, SmolmixError> {
Ok(self.inner.net.tcp_connect(addr).await?)
}
pub async fn udp_socket(&self) -> Result<UdpSocket, SmolmixError> {
let addr: SocketAddr = ([0, 0, 0, 0], 0).into();
Ok(self.inner.net.udp_bind(addr).await?)
}
pub async fn udp_socket_on(&self, port: u16) -> Result<UdpSocket, SmolmixError> {
let addr: SocketAddr = ([0, 0, 0, 0], port).into();
Ok(self.inner.net.udp_bind(addr).await?)
}
pub fn allocated_ips(&self) -> IpPair {
self.inner.allocated_ips
}
pub async fn shutdown(&self) {
let mut state = self.inner.shutdown.lock().await;
if let Some(s) = state.take() {
info!("Shutting down tunnel");
s.bridge_shutdown.shutdown();
let _ = s.bridge_handle.await;
info!("Tunnel shut down");
}
}
}