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