sentinelpass_protocol/
client.rs1use crate::envelope::{IpcEnvelope, Origin};
4use crate::error::ProtocolError;
5use crate::message::IpcMessage;
6use crate::token::load_ipc_token;
7use crate::Result;
8use std::path::PathBuf;
9#[allow(unused_imports)]
10use tokio::io::{AsyncReadExt, AsyncWriteExt};
11#[cfg(windows)]
12use tracing::debug;
13
14pub struct IpcClient {
16 socket_path: PathBuf,
17 auth_token: String,
18 client_token: Option<String>,
21 origin: Option<Origin>,
23}
24
25impl IpcClient {
26 pub fn new(socket_path: PathBuf) -> Result<Self> {
28 let auth_token = load_ipc_token()?;
29 Ok(Self::new_with_token(socket_path, auth_token))
30 }
31
32 pub fn new_with_token(socket_path: PathBuf, auth_token: String) -> Self {
34 Self {
35 socket_path,
36 auth_token,
37 client_token: None,
38 origin: None,
39 }
40 }
41
42 pub fn new_for_cli(socket_path: PathBuf, client_token: Option<String>) -> Result<Self> {
44 let auth_token = load_ipc_token()?;
45 Ok(Self {
46 socket_path,
47 auth_token,
48 client_token,
49 origin: Some(Origin::Cli),
50 })
51 }
52
53 pub fn with_context(mut self, client_token: Option<String>, origin: Option<Origin>) -> Self {
56 self.client_token = client_token;
57 self.origin = origin;
58 self
59 }
60
61 pub fn new_for_native_host(socket_path: PathBuf) -> Result<Self> {
63 let auth_token = load_ipc_token()?;
64 Ok(Self {
65 socket_path,
66 auth_token,
67 client_token: None,
68 origin: Some(Origin::NativeHost),
69 })
70 }
71
72 #[allow(unused_variables)]
74 pub async fn send(&self, msg: IpcMessage) -> Result<IpcMessage> {
75 #[cfg(unix)]
76 {
77 let mut conn =
79 crate::transport::unix::UnixSocketConnection::connect(self.socket_path.clone())
80 .await
81 .map_err(|e| {
82 ProtocolError::Ipc(format!("Failed to connect to daemon: {}", e))
83 })?;
84
85 let envelope = IpcEnvelope {
86 token: self.auth_token.clone(),
87 client_token: self.client_token.clone(),
88 origin: self.origin,
89 message: msg,
90 };
91 let msg_bytes = serde_json::to_vec(&envelope)
92 .map_err(|e| ProtocolError::Ipc(format!("Failed to serialize message: {}", e)))?;
93
94 conn.write_message(&msg_bytes)
95 .await
96 .map_err(|e| ProtocolError::Ipc(format!("Failed to write message: {}", e)))?;
97
98 let buffer = conn
100 .read_message()
101 .await
102 .map_err(|e| ProtocolError::Ipc(format!("Failed to read response: {}", e)))?;
103
104 serde_json::from_slice::<IpcMessage>(&buffer)
105 .map_err(|e| ProtocolError::Ipc(format!("Failed to parse response: {}", e)))
106 }
107
108 #[cfg(windows)]
109 {
110 let path_str = self.socket_path.to_string_lossy().to_string();
112 let use_tcp = path_str.starts_with("tcp://");
113
114 if use_tcp {
115 use tokio::net::TcpStream;
117
118 let addr_str = path_str.strip_prefix("tcp://").unwrap_or("127.0.0.1:35873");
119
120 let connect_deadline =
122 tokio::time::Instant::now() + tokio::time::Duration::from_secs(3);
123 let mut stream = loop {
124 match TcpStream::connect(addr_str).await {
125 Ok(s) => break s,
126 Err(e) => {
127 if e.kind() == std::io::ErrorKind::ConnectionRefused {
128 if tokio::time::Instant::now() >= connect_deadline {
129 return Err(ProtocolError::Ipc(format!(
130 "Failed to connect to daemon at {}: timed out after 3s",
131 addr_str
132 )));
133 }
134 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
135 continue;
136 }
137 return Err(ProtocolError::Ipc(format!(
138 "Failed to connect to daemon: {}",
139 e
140 )));
141 }
142 }
143 };
144
145 let envelope = IpcEnvelope {
146 token: self.auth_token.clone(),
147 client_token: self.client_token.clone(),
148 origin: self.origin,
149 message: msg,
150 };
151 let msg_bytes = serde_json::to_vec(&envelope).map_err(|e| {
152 ProtocolError::Ipc(format!("Failed to serialize message: {}", e))
153 })?;
154 let msg_bytes =
155 crate::windows_frame::encrypt_windows_ipc_frame(&self.auth_token, &msg_bytes)?;
156
157 let length = msg_bytes.len() as u32;
158
159 stream
160 .write_all(&length.to_be_bytes())
161 .await
162 .map_err(|e| ProtocolError::Ipc(format!("Failed to write length: {}", e)))?;
163
164 stream
165 .write_all(&msg_bytes)
166 .await
167 .map_err(|e| ProtocolError::Ipc(format!("Failed to write message: {}", e)))?;
168
169 stream
170 .flush()
171 .await
172 .map_err(|e| ProtocolError::Ipc(format!("Failed to flush: {}", e)))?;
173
174 let mut length_buf = [0u8; 4];
176 stream
177 .read_exact(&mut length_buf)
178 .await
179 .map_err(|e| ProtocolError::Ipc(format!("Failed to read length: {}", e)))?;
180
181 let response_length = u32::from_be_bytes(length_buf) as usize;
182
183 if response_length > 65536 {
184 return Err(ProtocolError::Ipc("Response too large".to_string()));
185 }
186
187 let mut buffer = vec![0u8; response_length];
188 stream
189 .read_exact(&mut buffer)
190 .await
191 .map_err(|e| ProtocolError::Ipc(format!("Failed to read response: {}", e)))?;
192
193 let buffer =
194 crate::windows_frame::decrypt_windows_ipc_frame(&self.auth_token, &buffer)?;
195
196 serde_json::from_slice::<IpcMessage>(&buffer)
197 .map_err(|e| ProtocolError::Ipc(format!("Failed to parse response: {}", e)))
198 } else {
199 let pipe_name = crate::windows_frame::windows_named_pipe_path();
201 debug!("Connecting to named pipe: {}", pipe_name);
202
203 let mut conn = crate::transport::windows::connect_named_pipe(&pipe_name, 3000)
204 .await
205 .map_err(|e| {
206 ProtocolError::Ipc(format!("Failed to connect to named pipe: {}", e))
207 })?;
208
209 let envelope = IpcEnvelope {
210 token: self.auth_token.clone(),
211 client_token: self.client_token.clone(),
212 origin: self.origin,
213 message: msg,
214 };
215 let msg_bytes = serde_json::to_vec(&envelope).map_err(|e| {
216 ProtocolError::Ipc(format!("Failed to serialize message: {}", e))
217 })?;
218 let msg_bytes =
219 crate::windows_frame::encrypt_windows_ipc_frame(&self.auth_token, &msg_bytes)?;
220
221 conn.write_message(&msg_bytes)
222 .await
223 .map_err(|e| ProtocolError::Ipc(format!("Failed to write message: {}", e)))?;
224
225 let buffer = conn
227 .read_message()
228 .await
229 .map_err(|e| ProtocolError::Ipc(format!("Failed to read response: {}", e)))?;
230
231 let buffer =
232 crate::windows_frame::decrypt_windows_ipc_frame(&self.auth_token, &buffer)?;
233
234 serde_json::from_slice::<IpcMessage>(&buffer)
235 .map_err(|e| ProtocolError::Ipc(format!("Failed to parse response: {}", e)))
236 }
237 }
238 }
239}