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}