cat_dev/
errors.rs

1//! A container for all the types of errors generated crate-wide.
2//!
3//! The top level error type is: [`CatBridgeError`], which wraps all the other
4//! types of errors. You can find more specific error types documented on each
5//! specific item.
6
7use crate::{
8	fsemul::errors::{FSEmulAPIError, FSEmulFSError, FSEmulProtocolError},
9	mion::errors::{MIONAPIError, MIONProtocolError},
10};
11use bytes::Bytes;
12use local_ip_address::Error as LocalIpAddressError;
13use miette::Diagnostic;
14use network_interface::Error as NetworkInterfaceError;
15use reqwest::Error as ReqwestError;
16use std::{ffi::FromBytesUntilNulError, str::Utf8Error, string::FromUtf8Error, time::Duration};
17use thiserror::Error;
18use tokio::{io::Error as IoError, sync::mpsc::error::SendError, task::JoinError};
19use walkdir::Error as WalkdirError;
20
21/// The 'top-level' error type for this entire crate, all error types
22/// wrap underneath this.
23#[derive(Error, Diagnostic, Debug)]
24pub enum CatBridgeError {
25	/// See [`APIError`] for details.
26	#[error(transparent)]
27	#[diagnostic(transparent)]
28	API(#[from] APIError),
29	/// We tried sending a message from one thread to another (within the same
30	/// process), but delivery could not be completed.
31	///
32	/// For more information on why this could fail please look at the associated
33	/// modules we may be using:
34	///
35	/// - [`std::sync::mpsc`]
36	/// - [`tokio::sync::mpsc`]
37	///
38	/// Each of these contain more information.
39	#[error("We could not send a message locally to another part of the process. This channel must've been closed unexpectedly.")]
40	#[diagnostic(code(cat_dev::closed_channel))]
41	ClosedChannel,
42	/// See [`FSError`] for details.
43	#[error(transparent)]
44	#[diagnostic(transparent)]
45	FS(#[from] FSError),
46	/// We spawned a background task, and for whatever reason we could not
47	/// wait for it to finish.
48	///
49	/// For the potential reasons for this, take a peek at [`tokio`]'s
50	/// documentation. Which is our asynchronous runtime.
51	#[error("We could not await an asynchronous task we spawned: {0:?}")]
52	#[diagnostic(code(cat_dev::join_failure))]
53	JoinFailure(#[from] JoinError),
54	/// See [`NetworkError`] for details.
55	#[error(transparent)]
56	#[diagnostic(transparent)]
57	Network(#[from] NetworkError),
58	/// We tried to spawn a task to run in the background, but couldn't.
59	///
60	/// For the potential reasons for this, take a peek at [`tokio`]'s
61	/// documentation. Which is our asynchronous runtime.
62	#[error("We could not spawn a task (a lightweight thread) to do work on.")]
63	#[diagnostic(code(cat_dev::spawn_failure))]
64	SpawnFailure(IoError),
65	#[error("This cat-dev API requires a 32 bit usize, and this machine does not have it, please upgrade your machine.")]
66	#[diagnostic(code(cat_dev::unsupported_bits_per_core))]
67	UnsupportedBitsPerCore,
68}
69
70/// An error that comes from one of our APIs, e.g. passing in a parameter
71/// that wasn't expected.
72///
73/// All the APIs within this crate will have errors will be collapsed under
74/// this particular error type. There will be no inner separation between
75/// modules.
76#[derive(Error, Diagnostic, Debug, PartialEq, Eq)]
77pub enum APIError {
78	#[error(transparent)]
79	#[diagnostic(transparent)]
80	FSEmul(#[from] FSEmulAPIError),
81	#[error(transparent)]
82	#[diagnostic(transparent)]
83	MION(#[from] MIONAPIError),
84	/// We failed to find our own hosts local IP address.
85	///
86	/// This usually means we don't have a network interface we can communicate
87	/// on that has an IPv4 address assigned.
88	#[error("We could not find the local hosts ipv4 address which is needed if an ip isn't explicitly passed in.")]
89	#[diagnostic(code(cat_dev::api::no_host_ip_found))]
90	NoHostIpFound,
91}
92
93/// Trying to interact with the filesystem has resulted in an error.
94#[derive(Error, Diagnostic, Debug)]
95pub enum FSError {
96	/// We need a place to read/store a list of all the bridges on your host.
97	///
98	/// However, if you see this we weren't able to automatically determine where
99	/// that file should go. Please either contribute a path for your OS to use,
100	/// or manually provide the host bridge path (this can only be done on the
101	/// newer versions of tools).
102	#[error("We can't find the path to store a complete list of host-bridges, please use explicit paths instead.")]
103	#[diagnostic(code(cat_dev::fs::cant_find_hostenv_path))]
104	CantFindHostEnvPath,
105	#[error(transparent)]
106	#[diagnostic(transparent)]
107	FSEmul(#[from] FSEmulFSError),
108	/// We expected to parse file as an INI data, but it did not contain valid
109	/// INI data.
110	#[error("Data read from the filesystem was expected to be a valid INI file: {0}")]
111	#[diagnostic(code(cat_dev::fs::expected_ini))]
112	InvalidDataNeedsToBeINI(String),
113	/// File "magic" are generally constants that should always be true.
114	#[error("Expected file magic of: {0}, got {1} as magic bytes")]
115	#[diagnostic(code(cat_dev::fs::invalid_file_magic))]
116	InvalidFileMagic(u32, u32),
117	/// Expected a file sized a specific amount of bytes, and it wasn't.
118	#[error("Expected file size of: {0} bytes, got a file sized {1} bytes")]
119	#[diagnostic(code(cat_dev::fs::invalid_file_size))]
120	InvalidFileSize(usize, usize),
121	/// See [`tokio::io::Error`] for details.
122	#[error("Error writing/reading data from the filesystem: {0}")]
123	#[diagnostic(code(cat_dev::fs::io))]
124	IO(#[from] IoError),
125	#[error("Error iterating through directory: {0:?}")]
126	#[diagnostic(code(cat_dev::fs::iterating_directory_error))]
127	IteratingDirectoryError(#[from] WalkdirError),
128	#[error("Expect file to have at least: {0} line(s), but it was only: {1} line(s) long.")]
129	#[diagnostic(code(cat_dev::fs::too_few_lines))]
130	TooFewLines(usize, usize),
131	/// The file can't be larger than a certain amount of bytes, and it was.
132	#[error("File cannot be larger than: {0} bytes, is {1} bytes")]
133	#[diagnostic(code(cat_dev::fs::too_large))]
134	TooLarge(usize, usize),
135	/// The file needs to be a certain amount of bytes, and it wasn't.
136	#[error("File needs to be at least: {0} bytes, is {1} bytes")]
137	#[diagnostic(code(cat_dev::fs::too_small))]
138	TooSmall(usize, usize),
139	/// We expected to read UTF-8 data from the filesystem, but it wasn't UTF-8.
140	#[error("Data read from the filesystem was expected to be UTF-8, but was not: {0}")]
141	#[diagnostic(code(cat_dev::fs::utf8_expected))]
142	Utf8Expected(#[from] FromUtf8Error),
143}
144
145/// Trying to interact with the network has resulted in an error.
146///
147/// *NOTE: this does not cover bogus data coming in from the network. This only
148/// covers errors related to interacting with the network. If you're looking
149/// for bogus data from the network errors look at [`NetworkParseError`].*
150#[derive(Error, Diagnostic, Debug)]
151pub enum NetworkError {
152	/// We failed to bind to a local address to listen for packets from the
153	/// network.
154	///
155	/// This can happen for numerous reason, such as:
156	///
157	/// - The program does not have permission to listen on this specific port.
158	/// - The address is already being used by another process.
159	/// - The network interface returned some type of error.
160	///
161	/// There are multiple other cases, but in general they're pretty OS
162	/// specific.
163	#[error("Failed to bind to a local address to receive packets.")]
164	#[diagnostic(code(cat_dev::net::bind_failure))]
165	BindFailure,
166	#[error("Expected some sort of data from other side, but got none.")]
167	#[diagnostic(code(cat_dev::net::expected_data))]
168	ExpectedData,
169	/// See [`reqwest::Error`] for details.
170	#[error("Underlying HTTP client error: {0}")]
171	#[diagnostic(code(cat_dev::net::http_failure))]
172	HTTP(#[from] ReqwestError),
173	/// See [`tokio::io::Error`] for details.
174	#[error("Error talking to the network could not send/receive data: {0}")]
175	#[diagnostic(code(cat_dev::net::io_error))]
176	IO(#[from] IoError),
177	/// See [`network_interface::Error::GetIfAddrsError`] for details.
178	#[error("Failed to list the network interfaces on your device: {0:?}.")]
179	#[diagnostic(code(cat_dev::net::list_interfaces_error))]
180	ListInterfacesFailure(NetworkInterfaceError),
181	/// See [`local_ip_address::Error`] for details.
182	#[error("Failure fetching local ip address: {0}")]
183	#[diagnostic(code(cat_dev::net::local_ip_failure))]
184	LocalIp(#[from] LocalIpAddressError),
185	/// See [`NetworkParseError`] for details.
186	#[error(transparent)]
187	#[diagnostic(transparent)]
188	Parse(#[from] NetworkParseError),
189	/// The client requested too many bytes to actively serve.
190	#[error("The client requested too many bytes to send over a connection at once (larger than usize::MAX on your architecture): {0}")]
191	#[diagnostic(code(cat_dev::net::requested_size_too_large))]
192	RequestedSizeTooLarge(u128),
193	/// If we failed to call `setsockopt` through libc.
194	///
195	/// For example if on linux see: <https://linux.die.net/man/2/setsockopt>
196	#[error("Failed to set the socket we're bound on as a broadcast address, this is needed to discover CAT devices.")]
197	#[diagnostic(code(cat_dev::net::set_broadcast_failure))]
198	SetBroadcastFailure,
199	/// Error adding a packet to a queue to send.
200	#[error("Error queueing up packet to be sent out over a conenction: {0:?}")]
201	#[diagnostic(code(cat_dev::net::send_queue_failure))]
202	SendQueueFailure(#[from] SendError<Bytes>),
203	/// We waited too long to send/receive data from the network.
204	///
205	/// There may be something wrong with our network connection, or the targets
206	/// network connection.
207	#[error(
208		"Timed out while writing/reading data from the network, failed to send and receive data."
209	)]
210	#[diagnostic(code(cat_dev::net::timeout))]
211	Timeout(Duration),
212}
213
214impl From<ReqwestError> for CatBridgeError {
215	fn from(value: ReqwestError) -> Self {
216		Self::Network(value.into())
217	}
218}
219
220impl From<SendError<Bytes>> for CatBridgeError {
221	fn from(value: SendError<Bytes>) -> Self {
222		Self::Network(value.into())
223	}
224}
225
226/// We tried parsing some data from the network, but failed to do so, someone
227/// sent us some junk.
228#[derive(Error, Diagnostic, Debug, PartialEq, Eq)]
229pub enum NetworkParseError {
230	/// Failed reading C String with NUL bytes at the end.
231	#[error("Failed reading c style string from packet: {0:?}")]
232	#[diagnostic(code(cat_dev::net::parse::bad_c_string))]
233	BadCString(#[from] FromBytesUntilNulError),
234	/// We expected to read a packet containing exactly a set of bytes,
235	/// unfortunatley it did not contain those _Exact_ bytes.
236	#[error("Tried to read Packet of type ({0}) from network, must be encoded exactly as [{1:02x?}], but got [{2:02x?}]")]
237	#[diagnostic(code(cat_dev::net::parse::doesnt_match_static_data))]
238	DoesntMatchStaticPayload(&'static str, &'static [u8], Bytes),
239	/// A field encoded within a packet was not correct (e.g. a string wasn't
240	/// UTF-8).
241	#[error("Reading Field {1} from Packet {0}, was not encoded correctly must be encoded as {2}")]
242	#[diagnostic(code(cat_dev::net::parse::field_encoded_incorrectly))]
243	FieldEncodedIncorrectly(&'static str, &'static str, &'static str),
244	/// A field encoded within a packet requires a minimum number of bytes, but
245	/// the field was not long enough.
246	#[error("Tried Reading Field {1} from Packet {0}. This Field requires at least {2} bytes, but only had {3}, bytes: {4:02x?}")]
247	#[diagnostic(code(cat_dev::net::parse::field_not_long_enough))]
248	FieldNotLongEnough(&'static str, &'static str, usize, usize, Bytes),
249	/// A field encoded within a packet has a maximum length that was exceeded.
250	#[error("Tried Reading Field {1} from Packet {0}. This field is at max {2} bytes, but had {3}, bytes: {4:02x?}")]
251	#[diagnostic(code(cat_dev::net::parse::field_too_long))]
252	FieldTooLong(&'static str, &'static str, usize, usize, Bytes),
253	#[error(transparent)]
254	#[diagnostic(transparent)]
255	FSEmul(#[from] FSEmulProtocolError),
256	/// Errors related to parsing MION specific protocols.
257	#[error(transparent)]
258	#[diagnostic(transparent)]
259	MION(#[from] MIONProtocolError),
260	/// The overall size of the packet was too short, and we cannot successfully
261	/// parse it.
262	#[error("Tried to read Packet of type ({0}) from network needs at least {1} bytes, but only got {2} bytes: {3:02x?}")]
263	#[diagnostic(code(cat_dev::net::parse::not_enough_data))]
264	NotEnoughData(&'static str, usize, usize, Bytes),
265	/// The overall size of the packet was too long, and there was unexpected
266	/// data at the end, a.k.a. the "Trailer".
267	#[error("Unexpected Trailer for Packet `{0}` received from the network (we're not sure what do with this extra data), extra bytes: {1:02x?}")]
268	#[diagnostic(code(cat_dev::net::parse::unexpected_trailer))]
269	UnexpectedTrailer(&'static str, Bytes),
270	/// We expected to read UTF-8 data from the network, but it wasn't UTF-8.
271	#[error("Data read from the network was expected to be UTF-8, but was not: {0}")]
272	#[diagnostic(code(cat_dev::net::parse::utf8_expected))]
273	Utf8Expected(#[from] FromUtf8Error),
274	/// We expected to read UTF-8 data from the network, but it wasn't UTF-8.
275	#[error("Data read from a network slice was expected to be UTF-8, but was not: {0}")]
276	#[diagnostic(code(cat_dev::net::parse::utf8_expected_slice))]
277	Utf8ExpectedSlice(#[from] Utf8Error),
278}
279impl From<NetworkParseError> for CatBridgeError {
280	fn from(value: NetworkParseError) -> Self {
281		Self::Network(value.into())
282	}
283}