1pub mod config;
2mod device;
3mod dns;
4mod proxy;
5mod stack;
6mod stream;
7mod tls;
8
9pub use config::ProxyConfig;
10
11use std::collections::HashMap;
12use std::net::Ipv4Addr;
13use std::num::NonZeroUsize;
14use std::os::unix::io::RawFd;
15use std::sync::{Arc, RwLock};
16
17use lru::LruCache;
18use proxy::ProxyEngine;
19use stack::NetworkStack;
20use tls::CertificateAuthority;
21use tokio::sync::mpsc;
22use tracing::info;
23
24pub type AllowedIps = Arc<RwLock<LruCache<Ipv4Addr, ()>>>;
32
33const ALLOWED_IPS_CAPACITY: usize = 1024;
36
37pub struct ProxyHandle {
39 _stack_thread: std::thread::JoinHandle<()>,
40 _runtime_thread: std::thread::JoinHandle<()>,
41 pub placeholders: HashMap<String, String>,
43 pub ca_cert_pem: Vec<u8>,
45}
46
47fn generate_placeholder() -> String {
49 use std::sync::atomic::{AtomicU64, Ordering};
50 use std::time::{SystemTime, UNIX_EPOCH};
51 static COUNTER: AtomicU64 = AtomicU64::new(0);
52 let ts = SystemTime::now()
53 .duration_since(UNIX_EPOCH)
54 .unwrap()
55 .as_nanos() as u64;
56 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
57 format!("shuru_tok_{:016x}{:04x}", ts, seq)
58}
59
60pub fn create_socketpair() -> anyhow::Result<(RawFd, RawFd)> {
63 let mut fds = [0i32; 2];
64 let ret = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_DGRAM, 0, fds.as_mut_ptr()) };
65 if ret != 0 {
66 return Err(anyhow::anyhow!(
67 "socketpair failed: {}",
68 std::io::Error::last_os_error()
69 ));
70 }
71
72 let host_fd = fds[1];
73
74 unsafe {
76 let sndbuf: libc::c_int = 1024 * 1024;
77 let rcvbuf: libc::c_int = 4 * 1024 * 1024;
78 libc::setsockopt(
79 host_fd,
80 libc::SOL_SOCKET,
81 libc::SO_SNDBUF,
82 &sndbuf as *const _ as _,
83 std::mem::size_of::<libc::c_int>() as _,
84 );
85 libc::setsockopt(
86 host_fd,
87 libc::SOL_SOCKET,
88 libc::SO_RCVBUF,
89 &rcvbuf as *const _ as _,
90 std::mem::size_of::<libc::c_int>() as _,
91 );
92 }
93
94 Ok((fds[0], fds[1]))
95}
96
97pub fn start(host_fd: RawFd, config: ProxyConfig) -> anyhow::Result<ProxyHandle> {
102 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
104
105 let ca = CertificateAuthority::new()?;
106 let ca_cert_pem = ca.ca_cert_pem();
107
108 let mut placeholders = HashMap::new();
110 for name in config.secrets.keys() {
111 placeholders.insert(name.clone(), generate_placeholder());
112 }
113
114 let allowed_ips: AllowedIps = Arc::new(RwLock::new(LruCache::new(
115 NonZeroUsize::new(ALLOWED_IPS_CAPACITY).expect("non-zero capacity"),
116 )));
117
118 let (event_tx, event_rx) = mpsc::unbounded_channel();
119 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
120
121 let stack_thread = std::thread::Builder::new()
122 .name("shuru-netstack".into())
123 .spawn(move || {
124 let mut stack = NetworkStack::new(host_fd, event_tx, cmd_rx);
125 stack.run();
126 })?;
127
128 let proxy_config = config;
129 let proxy_placeholders = placeholders.clone();
130 let proxy_allowed_ips = allowed_ips.clone();
131 let runtime_thread = std::thread::Builder::new()
132 .name("shuru-proxy".into())
133 .spawn(move || {
134 let rt = tokio::runtime::Builder::new_multi_thread()
135 .worker_threads(2)
136 .enable_all()
137 .build()
138 .expect("failed to create tokio runtime for proxy");
139
140 rt.block_on(async move {
141 let mut engine =
142 ProxyEngine::new(proxy_config, event_rx, cmd_tx, ca, proxy_placeholders, proxy_allowed_ips);
143 engine.run().await;
144 });
145 })?;
146
147 info!("proxy started");
148
149 Ok(ProxyHandle {
150 _stack_thread: stack_thread,
151 _runtime_thread: runtime_thread,
152 placeholders,
153 ca_cert_pem,
154 })
155}