1use std::io;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5pub enum FramingError {
6 #[error("unexpected EOF before message complete")]
7 UnexpectedEof,
8
9 #[error("message exceeds size limit ({received} bytes, limit {limit})")]
10 MessageTooLarge { limit: usize, received: usize },
11
12 #[error("invalid chunk size: {0:?}")]
13 InvalidChunkSize(String),
14
15 #[error("invalid framing header: expected {expected}, got {got:#02x?}")]
16 InvalidHeader {
17 expected: &'static str,
18 got: Vec<u8>,
19 },
20
21 #[error("I/O error: {0}")]
22 Io(#[from] std::io::Error),
23
24 #[error("invalid utf-8 in message")]
25 InvalidUtf8,
26}
27
28#[derive(Error, Debug)]
30pub enum TransportError {
31 #[error("Connection failed to {host}:{port}: {source}")]
33 ConnectionFailed {
34 host: String,
35 port: u16,
36 #[source]
37 source: io::Error,
38 },
39
40 #[error("SSH error: {0}")]
42 Ssh(#[from] russh::Error),
43
44 #[error("Authentication failed for user '{user}'")]
46 AuthenticationFailed { user: String },
47
48 #[error("SSH key error: {0}")]
50 Key(String),
51
52 #[error("Connection disconnected")]
54 Disconnected,
55
56 #[error("Operation timed out after {0:?}")]
58 Timeout(std::time::Duration),
59
60 #[error("Host key for {host}:{port} not found in known_hosts")]
62 HostKeyUnknown { host: String, port: u16 },
63
64 #[error(
66 "Host key for {host}:{port} has CHANGED (known_hosts line {line}). This could indicate a man-in-the-middle attack."
67 )]
68 HostKeyChanged {
69 host: String,
70 port: u16,
71 line: usize,
72 },
73
74 #[error("Known hosts error: {0}")]
76 KnownHosts(String),
77
78 #[error("I/O error: {0}")]
80 Io(#[from] io::Error),
81}
82
83#[derive(Debug, Error)]
84pub enum Error {
85 #[error("I/O error: {0}")]
86 Io(#[from] std::io::Error),
87
88 #[error("XML error: {0}")]
89 Xml(#[from] quick_xml::Error),
90
91 #[error("framing error: {0}")]
92 Framing(#[from] FramingError),
93
94 #[error("authentication failed")]
95 AuthFailed,
96
97 #[error("hello exchange failed: {0}")]
98 HelloFailed(String),
99
100 #[error("unexpected response: {0}")]
101 UnexpectedResponse(String),
102
103 #[error("RPC error (message-id={message_id}): {error}")]
104 Rpc { message_id: u32, error: String },
105
106 #[error("Transport error: {0}")]
107 Transport(#[from] TransportError),
108
109 #[error("invalid session state: expected Ready, got: {0}")]
110 InvalidState(String),
111
112 #[error("session closed")]
113 SessionClosed,
114}
115
116impl From<russh::Error> for Error {
119 fn from(e: russh::Error) -> Self {
120 Error::Transport(TransportError::Ssh(e))
121 }
122}
123
124pub type Result<T> = std::result::Result<T, Error>;