Skip to main content

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