1#![forbid(unsafe_code)]
2
3use super::{constants, default_config, utils::MaybeOwned, NonZeroByteSlice, NonZeroByteVec};
4
5use std::{borrow::Cow, path::Path};
6
7use cfg_if::cfg_if;
8use serde::{Serialize, Serializer};
9use typed_builder::TypedBuilder;
10
11#[derive(Copy, Clone, Debug)]
12pub(crate) enum Request {
13 Hello { version: u32 },
15
16 AliveCheck { request_id: u32 },
18
19 NewSession {
39 request_id: u32,
40 session: SessionZeroCopy,
41 },
42
43 OpenFwd { request_id: u32, fwd_mode: u32 },
49
50 CloseFwd { request_id: u32, fwd_mode: u32 },
53
54 StopListening { request_id: u32 },
60}
61impl Serialize for Request {
62 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63 use constants::*;
64 use Request::*;
65
66 match self {
67 Hello { version } => {
68 serializer.serialize_newtype_variant("Request", MUX_MSG_HELLO, "Hello", version)
69 }
70 AliveCheck { request_id } => serializer.serialize_newtype_variant(
71 "Request",
72 MUX_C_ALIVE_CHECK,
73 "AliveCheck",
74 request_id,
75 ),
76 NewSession {
77 request_id,
78 session,
79 } => serializer.serialize_newtype_variant(
80 "Request",
81 MUX_C_NEW_SESSION,
82 "NewSession",
83 &(*request_id, "", *session),
84 ),
85 OpenFwd {
86 request_id,
87 fwd_mode,
88 } => serializer.serialize_newtype_variant(
89 "Request",
90 MUX_C_OPEN_FWD,
91 "OpenFwd",
92 &(*request_id, fwd_mode),
93 ),
94 CloseFwd {
95 request_id,
96 fwd_mode,
97 } => serializer.serialize_newtype_variant(
98 "Request",
99 MUX_C_CLOSE_FWD,
100 "CloseFwd",
101 &(*request_id, fwd_mode),
102 ),
103 StopListening { request_id } => serializer.serialize_newtype_variant(
104 "Request",
105 MUX_C_STOP_LISTENING,
106 "StopListening",
107 request_id,
108 ),
109 }
110 }
111}
112
113#[derive(Copy, Clone, Debug, Serialize)]
115pub(crate) struct SessionZeroCopy {
116 pub tty: bool,
117
118 pub x11_forwarding: bool,
119
120 pub agent: bool,
121
122 pub subsystem: bool,
123
124 pub escape_ch: char,
125}
126
127#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, TypedBuilder)]
128#[builder(doc)]
129pub struct Session<'a> {
130 #[builder(default = false)]
131 pub tty: bool,
132
133 #[builder(default = false)]
134 pub x11_forwarding: bool,
135
136 #[builder(default = false)]
137 pub agent: bool,
138
139 #[builder(default = false)]
140 pub subsystem: bool,
141
142 #[builder(default = char::MAX)]
144 pub escape_ch: char,
145
146 #[builder(default_code = r#"Cow::Borrowed(default_config::get_term())"#)]
148 pub term: Cow<'a, NonZeroByteSlice>,
149 pub cmd: Cow<'a, NonZeroByteSlice>,
150}
151
152#[derive(Copy, Clone, Debug)]
153pub enum Fwd<'a> {
154 Local {
155 listen_socket: &'a Socket<'a>,
156 connect_socket: &'a Socket<'a>,
157 },
158 Remote {
159 listen_socket: &'a Socket<'a>,
160 connect_socket: &'a Socket<'a>,
161 },
162 Dynamic {
163 listen_socket: &'a Socket<'a>,
164 },
165}
166impl<'a> Fwd<'a> {
167 pub(crate) fn as_serializable(&self) -> (u32, &'a Socket<'a>, MaybeOwned<'a, Socket<'a>>) {
168 use Fwd::*;
169
170 match *self {
171 Local {
172 listen_socket,
173 connect_socket,
174 } => (
175 constants::MUX_FWD_LOCAL,
176 listen_socket,
177 MaybeOwned::Borrowed(connect_socket),
178 ),
179 Remote {
180 listen_socket,
181 connect_socket,
182 } => (
183 constants::MUX_FWD_REMOTE,
184 listen_socket,
185 MaybeOwned::Borrowed(connect_socket),
186 ),
187 Dynamic { listen_socket } => (
188 constants::MUX_FWD_DYNAMIC,
189 listen_socket,
190 MaybeOwned::Owned(Socket::UnixSocket {
191 path: Path::new("").into(),
192 }),
193 ),
194 }
195 }
196}
197impl<'a> Serialize for Fwd<'a> {
198 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
199 self.as_serializable().serialize(serializer)
200 }
201}
202
203trait PathExt {
204 fn to_non_null_bytes(&self) -> Cow<'_, NonZeroByteSlice>;
205
206 fn to_bytes(&self) -> Cow<'_, [u8]>;
207
208 fn to_string_lossy_and_as_bytes(&self) -> Cow<'_, [u8]>;
209}
210
211impl PathExt for Path {
212 fn to_non_null_bytes(&self) -> Cow<'_, NonZeroByteSlice> {
213 match self.to_bytes() {
214 Cow::Borrowed(slice) => NonZeroByteVec::from_bytes_slice_lossy(slice),
215 Cow::Owned(bytes) => Cow::Owned(NonZeroByteVec::from_bytes_remove_nul(bytes)),
216 }
217 }
218
219 fn to_bytes(&self) -> Cow<'_, [u8]> {
220 cfg_if! {
221 if #[cfg(unix)] {
222 use std::os::unix::ffi::OsStrExt;
223
224 Cow::Borrowed(self.as_os_str().as_bytes())
225 } else {
226 self.to_string_lossy_and_as_bytes()
227 }
228 }
229 }
230
231 fn to_string_lossy_and_as_bytes(&self) -> Cow<'_, [u8]> {
232 match self.to_string_lossy() {
233 Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
234 Cow::Owned(s) => Cow::Owned(s.into_bytes()),
235 }
236 }
237}
238
239#[derive(Clone, Debug, Eq, PartialEq, Hash)]
240pub enum Socket<'a> {
241 UnixSocket { path: Cow<'a, Path> },
242 TcpSocket { port: u32, host: Cow<'a, str> },
243}
244impl Socket<'_> {
245 pub(crate) fn as_serializable(&self) -> (Cow<'_, NonZeroByteSlice>, u32) {
246 use Socket::*;
247
248 let unix_socket_port: i32 = -2;
249
250 match self {
251 UnixSocket { path } => (path.to_non_null_bytes(), unix_socket_port as u32),
255 TcpSocket { port, host } => (
256 NonZeroByteVec::from_bytes_slice_lossy(host.as_bytes()),
257 *port,
258 ),
259 }
260 }
261}
262impl<'a> Serialize for Socket<'a> {
263 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
264 self.as_serializable().serialize(serializer)
265 }
266}