sandbox_runtime/sandbox/linux/
bridge.rs1use std::path::PathBuf;
4use std::process::Stdio;
5
6use tokio::process::{Child, Command};
7
8use crate::error::SandboxError;
9
10pub struct SocatBridge {
12 child: Option<Child>,
13 socket_path: PathBuf,
14}
15
16impl SocatBridge {
17 pub async fn unix_to_tcp(
21 socket_path: PathBuf,
22 tcp_host: &str,
23 tcp_port: u16,
24 ) -> Result<Self, SandboxError> {
25 if socket_path.exists() {
27 std::fs::remove_file(&socket_path)?;
28 }
29
30 let child = Command::new("socat")
31 .args([
32 &format!("UNIX-LISTEN:{},fork", socket_path.display()),
33 &format!("TCP:{}:{}", tcp_host, tcp_port),
34 ])
35 .stdin(Stdio::null())
36 .stdout(Stdio::null())
37 .stderr(Stdio::null())
38 .spawn()
39 .map_err(|e| {
40 if e.kind() == std::io::ErrorKind::NotFound {
41 SandboxError::MissingDependency(
42 "socat not found. Please install socat.".to_string(),
43 )
44 } else {
45 SandboxError::Io(e)
46 }
47 })?;
48
49 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
51
52 Ok(Self {
53 child: Some(child),
54 socket_path,
55 })
56 }
57
58 pub fn tcp_to_unix_command(tcp_port: u16, socket_path: &str) -> String {
61 format!(
62 "socat TCP-LISTEN:{},fork,reuseaddr UNIX-CONNECT:{}",
63 tcp_port, socket_path
64 )
65 }
66
67 pub fn socket_path(&self) -> &PathBuf {
69 &self.socket_path
70 }
71
72 pub async fn stop(&mut self) {
74 if let Some(ref mut child) = self.child {
75 let _ = child.kill().await;
76 }
77
78 if self.socket_path.exists() {
80 let _ = std::fs::remove_file(&self.socket_path);
81 }
82 }
83}
84
85impl Drop for SocatBridge {
86 fn drop(&mut self) {
87 if let Some(ref mut child) = self.child {
88 let _ = child.start_kill();
89 }
90
91 if self.socket_path.exists() {
92 let _ = std::fs::remove_file(&self.socket_path);
93 }
94 }
95}
96
97pub fn check_socat() -> bool {
99 std::process::Command::new("socat")
100 .arg("-V")
101 .output()
102 .map(|o| o.status.success())
103 .unwrap_or(false)
104}
105
106pub fn generate_socket_path(prefix: &str) -> PathBuf {
108 use rand::Rng;
109 let mut rng = rand::thread_rng();
110 let suffix: u32 = rng.gen();
111 PathBuf::from(format!("/tmp/{}-{}-{:08x}.sock", prefix, std::process::id(), suffix))
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_generate_socket_path() {
120 let path1 = generate_socket_path("srt-http");
121 let path2 = generate_socket_path("srt-http");
122
123 assert!(path1.to_string_lossy().starts_with("/tmp/srt-http-"));
124 assert!(path1.to_string_lossy().ends_with(".sock"));
125 assert_ne!(path1, path2);
127 }
128
129 #[test]
130 fn test_tcp_to_unix_command() {
131 let cmd = SocatBridge::tcp_to_unix_command(3128, "/tmp/http.sock");
132 assert_eq!(
133 cmd,
134 "socat TCP-LISTEN:3128,fork,reuseaddr UNIX-CONNECT:/tmp/http.sock"
135 );
136 }
137}