ssh_packet/connect.rs
1//! Messages involved in the SSH's **connect** (`SSH-CONNECT`) part of the protocol,
2//! as defined in the [RFC 4254](https://datatracker.ietf.org/doc/html/rfc4254).
3
4use std::num::NonZeroU32;
5
6use binrw::binrw;
7
8use crate::arch;
9
10/// The `SSH_MSG_GLOBAL_REQUEST` message.
11///
12/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-4>.
13#[binrw]
14#[derive(Debug, Clone)]
15#[brw(big, magic = 80_u8)]
16pub struct GlobalRequest<'b> {
17 #[bw(calc = context.as_ascii())]
18 kind: arch::Ascii<'b>,
19
20 /// Whether the sender wants a reply.
21 pub want_reply: arch::Bool,
22
23 /// The context of the global request.
24 #[br(args(kind))]
25 pub context: GlobalRequestContext<'b>,
26}
27
28/// The `context` in the `SSH_MSG_GLOBAL_REQUEST` message.
29#[binrw]
30#[derive(Debug, Clone)]
31#[brw(big)]
32#[br(import(kind: arch::Ascii<'_>))]
33pub enum GlobalRequestContext<'b> {
34 /// A request of type `tcpip-forward`,
35 /// as defined in [RFC4254 section 7.1](https://datatracker.ietf.org/doc/html/rfc4254#section-7.1).
36 #[br(pre_assert(kind == GlobalRequestContext::TCPIP_FORWARD))]
37 TcpipForward {
38 /// Address to bind on the remote.
39 bind_address: arch::Bytes<'b>,
40
41 /// Port to bind on the remote, randomly choosen if 0.
42 bind_port: u32,
43 },
44
45 /// A request of type `cancel-tcpip-forward`,
46 /// as defined in [RFC4254 section 7.1](https://datatracker.ietf.org/doc/html/rfc4254#section-7.1).
47 #[br(pre_assert(kind == GlobalRequestContext::CANCEL_TCPIP_FORWARD))]
48 CancelTcpipForward {
49 /// Address that was bound on the remote.
50 bind_address: arch::Bytes<'b>,
51
52 /// Port that was bound on the remote.
53 bind_port: u32,
54 },
55}
56
57impl GlobalRequestContext<'_> {
58 const TCPIP_FORWARD: arch::Ascii<'static> = arch::ascii!("tcpip-forward");
59 const CANCEL_TCPIP_FORWARD: arch::Ascii<'static> = arch::ascii!("cancel-tcpip-forward");
60
61 /// Get the [`GlobalRequestContext`]'s SSH identifier.
62 pub fn as_ascii(&self) -> arch::Ascii<'static> {
63 match self {
64 Self::TcpipForward { .. } => Self::TCPIP_FORWARD,
65 Self::CancelTcpipForward { .. } => Self::CANCEL_TCPIP_FORWARD,
66 }
67 }
68}
69/// The `SSH_MSG_REQUEST_SUCCESS` message (empty body).
70///
71/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-4>.
72#[binrw]
73#[derive(Debug, Clone)]
74#[brw(big, magic = 81_u8)]
75pub struct RequestSuccess;
76
77/// The `SSH_MSG_REQUEST_SUCCESS` message in the context of a `tcpip-forward` global request,
78/// if the provided port was `0` and `want_reply` was set to [`true`] in the request.
79///
80/// see [RFC4254 section 7.1](https://datatracker.ietf.org/doc/html/rfc4254#section-7.1).
81#[binrw]
82#[derive(Debug, Clone)]
83#[brw(big, magic = 81_u8)]
84pub struct ForwardingSuccess {
85 /// Port that was bound on the remote.
86 pub bound_port: u32,
87}
88
89/// The `SSH_MSG_REQUEST_FAILURE` message.
90///
91/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-4>.
92#[binrw]
93#[derive(Debug, Clone)]
94#[brw(big, magic = 82_u8)]
95pub struct RequestFailure;
96
97/// The `SSH_MSG_CHANNEL_OPEN` message.
98///
99/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.1>.
100#[binrw]
101#[derive(Debug, Clone)]
102#[brw(big, magic = 90_u8)]
103pub struct ChannelOpen<'b> {
104 #[bw(calc = context.as_ascii())]
105 kind: arch::Ascii<'b>,
106
107 /// Sender channel.
108 pub sender_channel: u32,
109
110 /// Initial window size, in bytes.
111 pub initial_window_size: u32,
112
113 /// Maximum packet size, in bytes.
114 pub maximum_packet_size: u32,
115
116 /// The context of the open request.
117 #[br(args(kind))]
118 pub context: ChannelOpenContext<'b>,
119}
120
121/// The `context` in the `SSH_MSG_CHANNEL_OPEN` message.
122#[binrw]
123#[derive(Debug, Clone)]
124#[brw(big)]
125#[br(import(kind: arch::Ascii<'_>))]
126pub enum ChannelOpenContext<'b> {
127 /// A channel of type `session`,
128 /// as defined in [RFC4254 section 6.1](https://datatracker.ietf.org/doc/html/rfc4254#section-6.1).
129 #[br(pre_assert(kind == ChannelOpenContext::SESSION))]
130 Session,
131
132 /// A channel of type `x11`,
133 /// as defined in [RFC4254 section 6.3.2](https://datatracker.ietf.org/doc/html/rfc4254#section-6.3.2).
134 #[br(pre_assert(kind == ChannelOpenContext::X11))]
135 X11 {
136 /// Originator address.
137 originator_address: arch::Ascii<'b>,
138
139 /// Originator port.
140 originator_port: u32,
141 },
142
143 /// A channel of type `forwarded-tcpip`,
144 /// as defined in [RFC4254 section 7.2](https://datatracker.ietf.org/doc/html/rfc4254#section-7.2).
145 #[br(pre_assert(kind == ChannelOpenContext::FORWARDED_TCPIP))]
146 ForwardedTcpip {
147 /// Address that was connected on the remote.
148 address: arch::Ascii<'b>,
149
150 /// Port that was connected on the remote.
151 port: u32,
152
153 /// Originator address.
154 originator_address: arch::Ascii<'b>,
155
156 /// Originator port.
157 originator_port: u32,
158 },
159
160 /// A channel of type `direct-tcpip`,
161 /// as defined in [RFC4254 section 7.2](https://datatracker.ietf.org/doc/html/rfc4254#section-7.2).
162 #[br(pre_assert(kind == ChannelOpenContext::DIRECT_TCPIP))]
163 DirectTcpip {
164 /// Address to connect to.
165 address: arch::Ascii<'b>,
166
167 /// Port to connect to.
168 port: u32,
169
170 /// Originator address.
171 originator_address: arch::Ascii<'b>,
172
173 /// Originator port.
174 originator_port: u32,
175 },
176}
177
178impl ChannelOpenContext<'_> {
179 const SESSION: arch::Ascii<'static> = arch::ascii!("session");
180 const X11: arch::Ascii<'static> = arch::ascii!("x11");
181 const FORWARDED_TCPIP: arch::Ascii<'static> = arch::ascii!("forwarded-tcpip");
182 const DIRECT_TCPIP: arch::Ascii<'static> = arch::ascii!("direct-tcpip");
183
184 /// Get the [`ChannelOpenContext`]'s SSH identifier.
185 pub fn as_ascii(&self) -> arch::Ascii<'static> {
186 match self {
187 Self::Session { .. } => Self::SESSION,
188 Self::X11 { .. } => Self::X11,
189 Self::ForwardedTcpip { .. } => Self::FORWARDED_TCPIP,
190 Self::DirectTcpip { .. } => Self::DIRECT_TCPIP,
191 }
192 }
193}
194
195/// The `SSH_MSG_CHANNEL_OPEN_CONFIRMATION` message.
196///
197/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.1>.
198#[binrw]
199#[derive(Debug, Clone)]
200#[brw(big, magic = 91_u8)]
201pub struct ChannelOpenConfirmation {
202 /// Recipient channel.
203 pub recipient_channel: u32,
204
205 /// Sender channel.
206 pub sender_channel: u32,
207
208 /// Initial window size, in bytes.
209 pub initial_window_size: u32,
210
211 /// Maximum packet size, in bytes.
212 pub maximum_packet_size: u32,
213}
214
215/// The `SSH_MSG_CHANNEL_OPEN_FAILURE` message.
216///
217/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.1>.
218#[binrw]
219#[derive(Debug, Clone)]
220#[brw(big, magic = 92_u8)]
221pub struct ChannelOpenFailure<'b> {
222 /// Recipient channel.
223 pub recipient_channel: u32,
224
225 /// Reason for the channel opening failure.
226 pub reason: ChannelOpenFailureReason,
227
228 /// Description of the reason.
229 pub description: arch::Utf8<'b>,
230
231 /// Language tag.
232 pub language: arch::Ascii<'b>,
233}
234
235/// The `reason` for failure in the `SSH_MSG_CHANNEL_OPEN_FAILURE` message.
236#[binrw]
237#[derive(Debug, Clone)]
238#[brw(big)]
239pub enum ChannelOpenFailureReason {
240 /// `SSH_OPEN_ADMINISTRATIVELY_PROHIBITED`.
241 #[brw(magic = 1_u32)]
242 AdministrativelyProhibited,
243
244 /// `SSH_OPEN_CONNECT_FAILED`.
245 #[brw(magic = 2_u32)]
246 ConnectFailed,
247
248 /// `SSH_OPEN_UNKNOWN_CHANNEL_TYPE`.
249 #[brw(magic = 3_u32)]
250 UnknownChannelType,
251
252 /// `SSH_OPEN_RESOURCE_SHORTAGE`.
253 #[brw(magic = 4_u32)]
254 ResourceShortage,
255
256 /// Any other failure reason, may be non-standard.
257 ///
258 /// The 'reason' values in the range of `0xFE000000`
259 /// through `0xFFFFFFFF` are reserved for PRIVATE USE.
260 Other(u32),
261}
262
263/// The `SSH_MSG_CHANNEL_WINDOW_ADJUST` message.
264///
265/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.2>.
266#[binrw]
267#[derive(Debug, Clone)]
268#[brw(big, magic = 93_u8)]
269pub struct ChannelWindowAdjust {
270 /// Recipient channel.
271 pub recipient_channel: u32,
272
273 /// Bytes to add to the window.
274 pub bytes_to_add: u32,
275}
276
277/// The `SSH_MSG_CHANNEL_DATA` message.
278///
279/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.2>.
280#[binrw]
281#[derive(Debug, Clone)]
282#[brw(big, magic = 94_u8)]
283pub struct ChannelData<'b> {
284 /// Recipient channel.
285 pub recipient_channel: u32,
286
287 /// Data bytes to transport.
288 pub data: arch::Bytes<'b>,
289}
290
291/// The `SSH_MSG_CHANNEL_EXTENDED_DATA` message.
292///
293/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.2>.
294#[binrw]
295#[derive(Debug, Clone)]
296#[brw(big, magic = 95_u8)]
297pub struct ChannelExtendedData<'b> {
298 /// Recipient channel.
299 pub recipient_channel: u32,
300
301 /// Type of the transmitted data, the value `1` is reserved for **stderr**.
302 pub data_type: NonZeroU32,
303
304 /// Data bytes to transport.
305 pub data: arch::Bytes<'b>,
306}
307
308/// The `SSH_MSG_CHANNEL_EOF` message.
309///
310/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>.
311#[binrw]
312#[derive(Debug, Clone)]
313#[brw(big, magic = 96_u8)]
314pub struct ChannelEof {
315 /// Recipient channel.
316 pub recipient_channel: u32,
317}
318
319/// The `SSH_MSG_CHANNEL_CLOSE` message.
320///
321/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.3>.
322#[binrw]
323#[derive(Debug, Clone)]
324#[brw(big, magic = 97_u8)]
325pub struct ChannelClose {
326 /// Recipient channel.
327 pub recipient_channel: u32,
328}
329
330/// The `SSH_MSG_CHANNEL_REQUEST` message.
331///
332/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.4>.
333#[binrw]
334#[derive(Debug, Clone)]
335#[brw(big, magic = 98_u8)]
336pub struct ChannelRequest<'b> {
337 /// Recipient channel.
338 pub recipient_channel: u32,
339
340 #[bw(calc = context.as_ascii())]
341 kind: arch::Ascii<'b>,
342
343 /// Whether the sender wants a reply.
344 pub want_reply: arch::Bool,
345
346 /// The context of the channel request.
347 #[br(args(kind))]
348 pub context: ChannelRequestContext<'b>,
349}
350
351/// The `context` in the `SSH_MSG_CHANNEL_REQUEST` message.
352#[binrw]
353#[derive(Debug, Clone)]
354#[brw(big)]
355#[br(import(kind: arch::Ascii<'_>))]
356pub enum ChannelRequestContext<'b> {
357 /// A request of type `pty-req`,
358 /// as defined in [RFC4254 section 6.2](https://datatracker.ietf.org/doc/html/rfc4254#section-6.2).
359 #[br(pre_assert(kind == ChannelRequestContext::PTY))]
360 Pty {
361 /// Peer's `$TERM` environment variable value.
362 term: arch::Bytes<'b>,
363
364 /// Terminal width, in columns.
365 width_chars: u32,
366
367 /// Terminal height, in rows.
368 height_chars: u32,
369
370 /// Terminal width, in pixels.
371 width_pixels: u32,
372
373 /// Terminal height, in pixels.
374 height_pixels: u32,
375
376 /// Encoded terminal modes.
377 modes: arch::Bytes<'b>,
378 },
379
380 /// A request of type `x11-req`,
381 /// as defined in [RFC4254 section 6.3](https://datatracker.ietf.org/doc/html/rfc4254#section-6.3).
382 #[br(pre_assert(kind == ChannelRequestContext::X11))]
383 X11 {
384 /// Whether only a single connection should be forwarded.
385 single_connection: arch::Bool,
386
387 /// X11 authentication protocol.
388 x11_authentication_protocol: arch::Bytes<'b>,
389
390 /// X11 authentication cookie.
391 x11_authentication_cookie: arch::Bytes<'b>,
392
393 /// X11 authentication number.
394 x11_screen_number: u32,
395 },
396
397 /// A request of type `env`,
398 /// as defined in [RFC4254 section 6.4](https://datatracker.ietf.org/doc/html/rfc4254#section-6.4).
399 #[br(pre_assert(kind == ChannelRequestContext::ENV))]
400 Env {
401 /// Environment variable name.
402 name: arch::Bytes<'b>,
403
404 /// Environment variable value.
405 value: arch::Bytes<'b>,
406 },
407
408 /// A request of type `shell`,
409 /// as defined in [RFC4254 section 6.5](https://datatracker.ietf.org/doc/html/rfc4254#section-6.5).
410 #[br(pre_assert(kind == ChannelRequestContext::SHELL))]
411 Shell,
412
413 /// A request of type `exec`,
414 /// as defined in [RFC4254 section 6.5](https://datatracker.ietf.org/doc/html/rfc4254#section-6.5).
415 #[br(pre_assert(kind == ChannelRequestContext::EXEC))]
416 Exec {
417 /// Command to be executed.
418 command: arch::Bytes<'b>,
419 },
420
421 /// A request of type `subsystem`,
422 /// as defined in [RFC4254 section 6.5](https://datatracker.ietf.org/doc/html/rfc4254#section-6.5).
423 #[br(pre_assert(kind == ChannelRequestContext::SUBSYSTEM))]
424 Subsystem {
425 /// Name of the requested subsystem.
426 name: arch::Bytes<'b>,
427 },
428
429 /// A request of type `window-change`,
430 /// as defined in [RFC4254 section 6.7](https://datatracker.ietf.org/doc/html/rfc4254#section-6.7).
431 #[br(pre_assert(kind == ChannelRequestContext::WINDOW_CHANGE))]
432 WindowChange {
433 /// Terminal width, in columns.
434 width_chars: u32,
435
436 /// Terminal height, in rows.
437 height_chars: u32,
438
439 /// Terminal width, in pixels.
440 width_pixels: u32,
441
442 /// Terminal height, in pixels.
443 height_pixels: u32,
444 },
445
446 /// A request of type `xon-xoff`,
447 /// as defined in [RFC4254 section 6.8](hhttps://datatracker.ietf.org/doc/html/rfc4254#section-6.8).
448 #[br(pre_assert(kind == ChannelRequestContext::XON_XOFF))]
449 XonXoff {
450 /// Whether the client is allowed to do flow control using `<CTRL>-<S>` and `<CTRL>-<Q>`.
451 client_can_do: arch::Bool,
452 },
453
454 /// A request of type `signal`,
455 /// as defined in [RFC4254 section 6.9](hhttps://datatracker.ietf.org/doc/html/rfc4254#section-6.9).
456 #[br(pre_assert(kind == ChannelRequestContext::SIGNAL))]
457 Signal {
458 /// Signal name (without the "SIG" prefix).
459 name: arch::Bytes<'b>,
460 },
461
462 /// A request of type `exit-status`,
463 /// as defined in [RFC4254 section 6.10](hhttps://datatracker.ietf.org/doc/html/rfc4254#section-6.10).
464 #[br(pre_assert(kind == ChannelRequestContext::EXIT_STATUS))]
465 ExitStatus {
466 /// Exit status, non-zero means failure.
467 code: u32,
468 },
469
470 /// A request of type `exit-signal`,
471 /// as defined in [RFC4254 section 6.10](hhttps://datatracker.ietf.org/doc/html/rfc4254#section-6.10).
472 #[br(pre_assert(kind == ChannelRequestContext::EXIT_SIGNAL))]
473 ExitSignal {
474 /// Signal name (without the "SIG" prefix).
475 name: arch::Bytes<'b>,
476
477 /// Whether a core dump is triggering the signal.
478 core_dumped: arch::Bool,
479
480 /// The error message for the signal.
481 error_message: arch::Utf8<'b>,
482
483 /// Language tag.
484 language: arch::Ascii<'b>,
485 },
486}
487
488impl ChannelRequestContext<'_> {
489 const PTY: arch::Ascii<'static> = arch::ascii!("pty-req");
490 const X11: arch::Ascii<'static> = arch::ascii!("x11-req");
491 const ENV: arch::Ascii<'static> = arch::ascii!("env");
492 const SHELL: arch::Ascii<'static> = arch::ascii!("shell");
493 const EXEC: arch::Ascii<'static> = arch::ascii!("exec");
494 const SUBSYSTEM: arch::Ascii<'static> = arch::ascii!("subsystem");
495 const WINDOW_CHANGE: arch::Ascii<'static> = arch::ascii!("window-change");
496 const XON_XOFF: arch::Ascii<'static> = arch::ascii!("xon-xoff");
497 const SIGNAL: arch::Ascii<'static> = arch::ascii!("signal");
498 const EXIT_STATUS: arch::Ascii<'static> = arch::ascii!("exit-status");
499 const EXIT_SIGNAL: arch::Ascii<'static> = arch::ascii!("exit-signal");
500
501 /// Get the [`ChannelRequestContext`]'s SSH identifier.
502 pub fn as_ascii(&self) -> arch::Ascii<'static> {
503 match self {
504 Self::Pty { .. } => Self::PTY,
505 Self::X11 { .. } => Self::X11,
506 Self::Env { .. } => Self::ENV,
507 Self::Shell { .. } => Self::SHELL,
508 Self::Exec { .. } => Self::EXEC,
509 Self::Subsystem { .. } => Self::SUBSYSTEM,
510 Self::WindowChange { .. } => Self::WINDOW_CHANGE,
511 Self::XonXoff { .. } => Self::XON_XOFF,
512 Self::Signal { .. } => Self::SIGNAL,
513 Self::ExitStatus { .. } => Self::EXIT_STATUS,
514 Self::ExitSignal { .. } => Self::EXIT_SIGNAL,
515 }
516 }
517}
518
519/// The `SSH_MSG_CHANNEL_SUCCESS` message.
520///
521/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.4>.
522#[binrw]
523#[derive(Debug, Clone)]
524#[brw(big, magic = 99_u8)]
525pub struct ChannelSuccess {
526 /// Recipient channel.
527 pub recipient_channel: u32,
528}
529
530/// The `SSH_MSG_CHANNEL_FAILURE` message.
531///
532/// see <https://datatracker.ietf.org/doc/html/rfc4254#section-5.4>.
533#[binrw]
534#[derive(Debug, Clone)]
535#[brw(big, magic = 100_u8)]
536pub struct ChannelFailure {
537 /// Recipient channel.
538 pub recipient_channel: u32,
539}