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::os::unix::io::RawFd;
13
14use proxy::ProxyEngine;
15use stack::NetworkStack;
16use tls::CertificateAuthority;
17use tokio::sync::mpsc;
18use tracing::info;
19
20pub struct ProxyHandle {
22 _stack_thread: std::thread::JoinHandle<()>,
23 _runtime_thread: std::thread::JoinHandle<()>,
24 pub placeholders: HashMap<String, String>,
26 pub ca_cert_pem: Vec<u8>,
28}
29
30fn generate_placeholder() -> String {
32 use std::sync::atomic::{AtomicU64, Ordering};
33 use std::time::{SystemTime, UNIX_EPOCH};
34 static COUNTER: AtomicU64 = AtomicU64::new(0);
35 let ts = SystemTime::now()
36 .duration_since(UNIX_EPOCH)
37 .unwrap()
38 .as_nanos() as u64;
39 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
40 format!("shuru_tok_{:016x}{:04x}", ts, seq)
41}
42
43pub fn create_socketpair() -> anyhow::Result<(RawFd, RawFd)> {
46 let mut fds = [0i32; 2];
47 let ret = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_DGRAM, 0, fds.as_mut_ptr()) };
48 if ret != 0 {
49 return Err(anyhow::anyhow!(
50 "socketpair failed: {}",
51 std::io::Error::last_os_error()
52 ));
53 }
54
55 let host_fd = fds[1];
56
57 unsafe {
59 let sndbuf: libc::c_int = 1024 * 1024;
60 let rcvbuf: libc::c_int = 4 * 1024 * 1024;
61 libc::setsockopt(
62 host_fd,
63 libc::SOL_SOCKET,
64 libc::SO_SNDBUF,
65 &sndbuf as *const _ as _,
66 std::mem::size_of::<libc::c_int>() as _,
67 );
68 libc::setsockopt(
69 host_fd,
70 libc::SOL_SOCKET,
71 libc::SO_RCVBUF,
72 &rcvbuf as *const _ as _,
73 std::mem::size_of::<libc::c_int>() as _,
74 );
75 }
76
77 Ok((fds[0], fds[1]))
78}
79
80pub fn start(host_fd: RawFd, config: ProxyConfig) -> anyhow::Result<ProxyHandle> {
85 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
87
88 let ca = CertificateAuthority::new()?;
89 let ca_cert_pem = ca.ca_cert_pem();
90
91 let mut placeholders = HashMap::new();
93 for name in config.secrets.keys() {
94 placeholders.insert(name.clone(), generate_placeholder());
95 }
96
97 let (event_tx, event_rx) = mpsc::unbounded_channel();
98 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
99
100 let stack_thread = std::thread::Builder::new()
101 .name("shuru-netstack".into())
102 .spawn(move || {
103 let mut stack = NetworkStack::new(host_fd, event_tx, cmd_rx);
104 stack.run();
105 })?;
106
107 let proxy_config = config;
108 let proxy_placeholders = placeholders.clone();
109 let runtime_thread = std::thread::Builder::new()
110 .name("shuru-proxy".into())
111 .spawn(move || {
112 let rt = tokio::runtime::Builder::new_multi_thread()
113 .worker_threads(2)
114 .enable_all()
115 .build()
116 .expect("failed to create tokio runtime for proxy");
117
118 rt.block_on(async move {
119 let mut engine =
120 ProxyEngine::new(proxy_config, event_rx, cmd_tx, ca, proxy_placeholders);
121 engine.run().await;
122 });
123 })?;
124
125 info!("proxy started");
126
127 Ok(ProxyHandle {
128 _stack_thread: stack_thread,
129 _runtime_thread: runtime_thread,
130 placeholders,
131 ca_cert_pem,
132 })
133}