1#[cfg(unix)]
6use crate::connection::socket::Command;
7#[cfg(windows)]
8use crate::win32::autolaunch_bus_address;
9use crate::{Error, Result};
10#[cfg(not(feature = "tokio"))]
11use async_io::Async;
12use std::collections::HashMap;
13#[cfg(not(feature = "tokio"))]
14use std::net::TcpStream;
15#[cfg(unix)]
16use std::os::unix::net::{SocketAddr, UnixStream};
17#[cfg(feature = "tokio")]
18use tokio::net::TcpStream;
19#[cfg(feature = "tokio-vsock")]
20use tokio_vsock::VsockStream;
21#[cfg(windows)]
22use uds_windows::UnixStream;
23#[cfg(all(feature = "vsock", not(feature = "tokio")))]
24use vsock::VsockStream;
25#[cfg(unix)]
26mod unixexec;
27#[cfg(unix)]
28pub use unixexec::Unixexec;
29
30use std::{
31 fmt::{Display, Formatter},
32 str::from_utf8_unchecked,
33};
34
35mod unix;
36pub use unix::{Unix, UnixSocket};
37mod tcp;
38pub use tcp::{Tcp, TcpTransportFamily};
39#[cfg(windows)]
40mod autolaunch;
41#[cfg(windows)]
42pub use autolaunch::{Autolaunch, AutolaunchScope};
43#[cfg(target_os = "macos")]
44mod launchd;
45#[cfg(target_os = "macos")]
46pub use launchd::Launchd;
47#[cfg(unix)]
48mod ibus;
49#[cfg(unix)]
50pub use ibus::Ibus;
51#[cfg(any(
52 all(feature = "vsock", not(feature = "tokio")),
53 feature = "tokio-vsock"
54))]
55#[path = "vsock.rs"]
56mod vsock_transport;
58#[cfg(target_os = "linux")]
59use std::os::linux::net::SocketAddrExt;
60#[cfg(any(
61 all(feature = "vsock", not(feature = "tokio")),
62 feature = "tokio-vsock"
63))]
64pub use vsock_transport::Vsock;
65
66#[derive(Clone, Debug, PartialEq, Eq)]
68#[non_exhaustive]
69pub enum Transport {
70 Unix(Unix),
72 Tcp(Tcp),
74 #[cfg(windows)]
76 Autolaunch(Autolaunch),
77 #[cfg(target_os = "macos")]
79 Launchd(Launchd),
80 #[cfg(unix)]
85 Ibus(Ibus),
86 #[cfg(any(
87 all(feature = "vsock", not(feature = "tokio")),
88 feature = "tokio-vsock"
89 ))]
90 Vsock(Vsock),
96 #[cfg(unix)]
98 Unixexec(Unixexec),
99}
100
101impl Transport {
102 #[cfg_attr(any(unix, windows), async_recursion::async_recursion)]
103 pub(super) async fn connect(self) -> Result<Stream> {
104 match self {
105 Transport::Unix(unix) => {
106 let addr = match unix.take_path() {
109 #[cfg(unix)]
110 UnixSocket::File(path) => SocketAddr::from_pathname(path)?,
111 #[cfg(windows)]
112 UnixSocket::File(path) => path,
113 #[cfg(target_os = "linux")]
114 UnixSocket::Abstract(name) => {
115 SocketAddr::from_abstract_name(name.as_encoded_bytes())?
116 }
117 UnixSocket::Dir(_) | UnixSocket::TmpDir(_) => {
118 return Err(Error::Unsupported);
120 }
121 };
122 let stream = crate::Task::spawn_blocking(
123 move || -> Result<_> {
124 #[cfg(unix)]
125 let stream = UnixStream::connect_addr(&addr)?;
126 #[cfg(windows)]
127 let stream = UnixStream::connect(addr)?;
128 stream.set_nonblocking(true)?;
129
130 Ok(stream)
131 },
132 "unix stream connection",
133 )
134 .await??;
135 #[cfg(not(feature = "tokio"))]
136 {
137 Async::new(stream)
138 .map(Stream::Unix)
139 .map_err(|e| Error::InputOutput(e.into()))
140 }
141
142 #[cfg(feature = "tokio")]
143 {
144 #[cfg(unix)]
145 {
146 tokio::net::UnixStream::from_std(stream)
147 .map(Stream::Unix)
148 .map_err(|e| Error::InputOutput(e.into()))
149 }
150
151 #[cfg(not(unix))]
152 {
153 let _ = stream;
154 Err(Error::Unsupported)
155 }
156 }
157 }
158 #[cfg(unix)]
159 Transport::Unixexec(unixexec) => unixexec.connect().await.map(Stream::Unixexec),
160 #[cfg(all(feature = "vsock", not(feature = "tokio")))]
161 Transport::Vsock(addr) => {
162 let stream = VsockStream::connect_with_cid_port(addr.cid(), addr.port())?;
163 Async::new(stream).map(Stream::Vsock).map_err(Into::into)
164 }
165
166 #[cfg(feature = "tokio-vsock")]
167 Transport::Vsock(addr) => {
168 VsockStream::connect(tokio_vsock::VsockAddr::new(addr.cid(), addr.port()))
169 .await
170 .map(Stream::Vsock)
171 .map_err(Into::into)
172 }
173
174 Transport::Tcp(mut addr) => match addr.take_nonce_file() {
175 Some(nonce_file) => {
176 #[allow(unused_mut)]
177 let mut stream = addr.connect().await?;
178
179 #[cfg(unix)]
180 let nonce_file = {
181 use std::os::unix::ffi::OsStrExt;
182 std::ffi::OsStr::from_bytes(&nonce_file)
183 };
184
185 #[cfg(windows)]
186 let nonce_file = std::str::from_utf8(&nonce_file).map_err(|_| {
187 Error::Address("nonce file path is invalid UTF-8".to_owned())
188 })?;
189
190 #[cfg(not(feature = "tokio"))]
191 {
192 let nonce = std::fs::read(nonce_file)?;
193 let mut nonce = &nonce[..];
194
195 while !nonce.is_empty() {
196 let len = stream
197 .write_with(|mut s| std::io::Write::write(&mut s, nonce))
198 .await?;
199 nonce = &nonce[len..];
200 }
201 }
202
203 #[cfg(feature = "tokio")]
204 {
205 let nonce = tokio::fs::read(nonce_file).await?;
206 tokio::io::AsyncWriteExt::write_all(&mut stream, &nonce).await?;
207 }
208
209 Ok(Stream::Tcp(stream))
210 }
211 None => addr.connect().await.map(Stream::Tcp),
212 },
213
214 #[cfg(windows)]
215 Transport::Autolaunch(Autolaunch { scope }) => match scope {
216 Some(_) => Err(Error::Address(
217 "Autolaunch scopes are currently unsupported".to_owned(),
218 )),
219 None => {
220 let addr = autolaunch_bus_address()?;
221 addr.connect().await
222 }
223 },
224
225 #[cfg(target_os = "macos")]
226 Transport::Launchd(launchd) => {
227 let addr = launchd.bus_address().await?;
228 addr.connect().await
229 }
230
231 #[cfg(unix)]
232 Transport::Ibus(ibus) => {
233 let addr = ibus.bus_address().await?;
234 addr.connect().await
235 }
236 }
237 }
238
239 pub(super) fn from_options(transport: &str, options: HashMap<&str, &str>) -> Result<Self> {
241 match transport {
242 "unix" => Unix::from_options(options).map(Self::Unix),
243 #[cfg(unix)]
244 "unixexec" => Unixexec::from_options(options).map(Self::Unixexec),
245 "tcp" => Tcp::from_options(options, false).map(Self::Tcp),
246 "nonce-tcp" => Tcp::from_options(options, true).map(Self::Tcp),
247 #[cfg(any(
248 all(feature = "vsock", not(feature = "tokio")),
249 feature = "tokio-vsock"
250 ))]
251 "vsock" => Vsock::from_options(options).map(Self::Vsock),
252 #[cfg(windows)]
253 "autolaunch" => Autolaunch::from_options(options).map(Self::Autolaunch),
254 #[cfg(target_os = "macos")]
255 "launchd" => Launchd::from_options(options).map(Self::Launchd),
256 #[cfg(unix)]
257 "ibus" => Ibus::from_options(options).map(Self::Ibus),
258
259 _ => Err(Error::Address(format!(
260 "unsupported transport '{transport}'"
261 ))),
262 }
263 }
264}
265
266#[cfg(not(feature = "tokio"))]
267#[derive(Debug)]
268pub(crate) enum Stream {
269 Unix(Async<UnixStream>),
270 #[cfg(unix)]
271 Unixexec(Command),
272 Tcp(Async<TcpStream>),
273 #[cfg(feature = "vsock")]
274 Vsock(Async<VsockStream>),
275}
276
277#[cfg(feature = "tokio")]
278#[derive(Debug)]
279pub(crate) enum Stream {
280 #[cfg(unix)]
281 Unix(tokio::net::UnixStream),
282 #[cfg(unix)]
283 Unixexec(Command),
284 Tcp(TcpStream),
285 #[cfg(feature = "tokio-vsock")]
286 Vsock(VsockStream),
287}
288
289fn decode_hex(c: char) -> Result<u8> {
290 match c {
291 '0'..='9' => Ok(c as u8 - b'0'),
292 'a'..='f' => Ok(c as u8 - b'a' + 10),
293 'A'..='F' => Ok(c as u8 - b'A' + 10),
294
295 _ => Err(Error::Address(
296 "invalid hexadecimal character in percent-encoded sequence".to_owned(),
297 )),
298 }
299}
300
301pub(crate) fn decode_percents(value: &str) -> Result<Vec<u8>> {
302 let mut iter = value.chars();
303 let mut decoded = Vec::new();
304
305 while let Some(c) = iter.next() {
306 if matches!(c, '-' | '0'..='9' | 'A'..='Z' | 'a'..='z' | '_' | '/' | '.' | '\\' | '*') {
307 decoded.push(c as u8)
308 } else if c == '%' {
309 decoded.push(
310 (decode_hex(iter.next().ok_or_else(|| {
311 Error::Address("incomplete percent-encoded sequence".to_owned())
312 })?)?
313 << 4)
314 | decode_hex(iter.next().ok_or_else(|| {
315 Error::Address("incomplete percent-encoded sequence".to_owned())
316 })?)?,
317 );
318 } else {
319 return Err(Error::Address("Invalid character in address".to_owned()));
320 }
321 }
322
323 Ok(decoded)
324}
325
326pub(super) fn encode_percents(f: &mut Formatter<'_>, mut value: &[u8]) -> std::fmt::Result {
327 const LOOKUP: &str = "\
328%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f\
329%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f\
330%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f\
331%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f\
332%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f\
333%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f\
334%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f\
335%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e%7f\
336%80%81%82%83%84%85%86%87%88%89%8a%8b%8c%8d%8e%8f\
337%90%91%92%93%94%95%96%97%98%99%9a%9b%9c%9d%9e%9f\
338%a0%a1%a2%a3%a4%a5%a6%a7%a8%a9%aa%ab%ac%ad%ae%af\
339%b0%b1%b2%b3%b4%b5%b6%b7%b8%b9%ba%bb%bc%bd%be%bf\
340%c0%c1%c2%c3%c4%c5%c6%c7%c8%c9%ca%cb%cc%cd%ce%cf\
341%d0%d1%d2%d3%d4%d5%d6%d7%d8%d9%da%db%dc%dd%de%df\
342%e0%e1%e2%e3%e4%e5%e6%e7%e8%e9%ea%eb%ec%ed%ee%ef\
343%f0%f1%f2%f3%f4%f5%f6%f7%f8%f9%fa%fb%fc%fd%fe%ff";
344
345 loop {
346 let pos = value.iter().position(
347 |c| !matches!(c, b'-' | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_' | b'/' | b'.' | b'\\' | b'*'),
348 );
349
350 if let Some(pos) = pos {
351 f.write_str(unsafe { from_utf8_unchecked(&value[..pos]) })?;
354
355 let c = value[pos];
356 value = &value[pos + 1..];
357
358 let pos = c as usize * 3;
359 f.write_str(&LOOKUP[pos..pos + 3])?;
360 } else {
361 f.write_str(unsafe { from_utf8_unchecked(value) })?;
364 return Ok(());
365 }
366 }
367}
368
369impl Display for Transport {
370 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
371 match self {
372 Self::Tcp(tcp) => write!(f, "{tcp}")?,
373 Self::Unix(unix) => write!(f, "{unix}")?,
374 #[cfg(unix)]
375 Self::Unixexec(unixexec) => write!(f, "{unixexec}")?,
376 #[cfg(any(
377 all(feature = "vsock", not(feature = "tokio")),
378 feature = "tokio-vsock"
379 ))]
380 Self::Vsock(vsock) => write!(f, "{}", vsock)?,
381 #[cfg(windows)]
382 Self::Autolaunch(autolaunch) => write!(f, "{autolaunch}")?,
383 #[cfg(target_os = "macos")]
384 Self::Launchd(launchd) => write!(f, "{launchd}")?,
385 #[cfg(unix)]
386 Self::Ibus(ibus) => write!(f, "{ibus}")?,
387 }
388
389 Ok(())
390 }
391}