1use std::collections::HashMap;
2use std::net::SocketAddr;
3use std::sync::{LazyLock, RwLock};
4
5use async_trait::async_trait;
6
7pub mod netns;
8pub mod tcp;
9
10#[async_trait]
11pub trait NetworkIsolator: Send + Sync {
12 async fn setup(&self) -> anyhow::Result<()>;
13
14 async fn spawn_proxy(
15 &self,
16 target_ip: &str,
17 target_port: u16,
18 ) -> anyhow::Result<(ProxyListener, tokio::task::JoinHandle<()>)>;
19
20 async fn teardown(&self) -> anyhow::Result<()>;
21}
22
23pub struct ProxyListener {
24 pub local_addr: SocketAddr,
25 pub target_addr: SocketAddr,
26}
27
28static REGISTRY: LazyLock<RwLock<HashMap<String, SocketAddr>>> =
30 LazyLock::new(|| RwLock::new(HashMap::new()));
31
32pub fn bind_proxy(target: &str, local: SocketAddr) {
33 if let Ok(mut g) = REGISTRY.write() {
34 g.insert(target.to_string(), local);
35 }
36}
37
38pub fn unbind_proxy(target: &str) {
39 if let Ok(mut g) = REGISTRY.write() {
40 g.remove(target);
41 }
42}
43
44pub fn is_proxied(target: &str) -> bool {
45 REGISTRY
46 .read()
47 .map(|g| g.contains_key(target))
48 .unwrap_or(false)
49}
50
51pub fn resolve_dial(host: &str, port: u16) -> String {
53 if let Ok(g) = REGISTRY.read() {
54 if let Some(addr) = g.get(host) {
55 return addr.to_string();
56 }
57 }
58 format!("{host}:{port}")
59}