implement custom error type

This commit is contained in:
glyph 2021-11-30 13:48:16 +02:00
parent 2429ea8fdd
commit cd8e5737c4
5 changed files with 269 additions and 119 deletions

1
Cargo.lock generated
View File

@ -2638,7 +2638,6 @@ dependencies = [
"regex", "regex",
"serde 1.0.130", "serde 1.0.130",
"serde_json", "serde_json",
"snafu 0.6.10",
"wpactrl", "wpactrl",
] ]

View File

@ -36,7 +36,6 @@ log = "0.4"
probes = "0.4" probes = "0.4"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
snafu = "0.6"
regex = "1" regex = "1"
wpactrl = "0.3.1" wpactrl = "0.3.1"

View File

@ -1,150 +1,293 @@
use std::{error, io, str}; use std::{error, io, str};
use jsonrpc_core::{types::error::Error, ErrorCode}; use jsonrpc_core::{types::error::Error as JsonRpcError, ErrorCode};
use probes::ProbeError; use probes::ProbeError;
use serde_json::error::Error as SerdeError; use serde_json::error::Error as SerdeError;
use snafu::Snafu;
pub type BoxError = Box<dyn error::Error>; #[derive(Debug)]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum NetworkError { pub enum NetworkError {
#[snafu(display("{}", err_msg))] Add {
ActivateAp { err_msg: String }, ssid: String,
},
#[snafu(display("{}", err_msg))] NoState {
ActivateClient { err_msg: String }, iface: String,
source: io::Error,
},
#[snafu(display("Failed to add network for {}", ssid))] Disable {
Add { ssid: String }, id: String,
iface: String,
},
#[snafu(display("Failed to retrieve state for interface: {}", iface))] Disconnect {
NoState { iface: String, source: io::Error }, iface: String,
},
#[snafu(display("Failed to disable network {} for interface: {}", id, iface))] GenWpaPassphrase {
Disable { id: String, iface: String }, ssid: String,
source: io::Error,
},
#[snafu(display("Failed to disconnect {}", iface))] GenWpaPassphraseWarning {
Disconnect { iface: String }, ssid: String,
err_msg: String,
},
#[snafu(display("Failed to generate wpa passphrase for {}: {}", ssid, source))] Id {
GenWpaPassphrase { ssid: String, source: io::Error }, ssid: String,
iface: String,
},
#[snafu(display("Failed to generate wpa passphrase for {}: {}", ssid, err_msg))] NoIp {
GenWpaPassphraseWarning { ssid: String, err_msg: String }, iface: String,
source: io::Error,
},
#[snafu(display("No ID found for {} on interface: {}", ssid, iface))] Rssi {
Id { ssid: String, iface: String }, iface: String,
},
#[snafu(display("Could not access IP address for interface: {}", iface))] RssiPercent {
NoIp { iface: String, source: io::Error }, iface: String,
},
#[snafu(display("Could not find RSSI for interface: {}", iface))] Ssid {
Rssi { iface: String }, iface: String,
},
#[snafu(display("Could not find signal quality (%) for interface: {}", iface))] State {
RssiPercent { iface: String }, iface: String,
},
#[snafu(display("Could not find SSID for interface: {}", iface))] Status {
Ssid { iface: String }, iface: String,
},
#[snafu(display("No state found for interface: {}", iface))] Traffic {
State { iface: String }, iface: String,
},
#[snafu(display("No status found for interface: {}", iface))]
Status { iface: String },
#[snafu(display("Could not find network traffic for interface: {}", iface))]
Traffic { iface: String },
#[snafu(display("No saved networks found for default interface"))]
SavedNetworks, SavedNetworks,
#[snafu(display("No networks found in range of interface: {}", iface))] AvailableNetworks {
AvailableNetworks { iface: String }, iface: String,
},
#[snafu(display("Missing expected parameters: {}", e))] MissingParams(JsonRpcError),
MissingParams { e: Error },
#[snafu(display("Failed to set new password for network {} on {}", id, iface))] Modify {
Modify { id: String, iface: String }, id: String,
iface: String,
},
#[snafu(display("No IP found for interface: {}", iface))] Ip {
Ip { iface: String }, iface: String,
},
#[snafu(display("Failed to parse integer from string for RSSI value: {}", source))] ParseString(std::num::ParseIntError),
ParseString { source: std::num::ParseIntError },
#[snafu(display( NoTraffic {
"Failed to retrieve network traffic measurement for {}: {}", iface: String,
iface, source: ProbeError,
source },
))]
NoTraffic { iface: String, source: ProbeError },
#[snafu(display("Failed to reassociate with WiFi network for interface: {}", iface))] Reassociate {
Reassociate { iface: String }, iface: String,
},
#[snafu(display("Failed to force reread of wpa_supplicant configuration file"))]
Reconfigure, Reconfigure,
#[snafu(display("Failed to reconnect with WiFi network for interface: {}", iface))] Reconnect {
Reconnect { iface: String }, iface: String,
},
#[snafu(display("Regex command failed"))] Regex(regex::Error),
Regex { source: regex::Error },
#[snafu(display("Failed to delete network {} for interface: {}", id, iface))] Delete {
Delete { id: String, iface: String }, id: String,
iface: String,
},
#[snafu(display("Failed to retrieve state of wlan0 service: {}", source))] WlanState(io::Error),
WlanState { source: io::Error },
#[snafu(display("Failed to retrieve connection state of wlan0 interface: {}", source))] WlanOperstate(io::Error),
WlanOperstate { source: io::Error },
#[snafu(display("Failed to save configuration changes to file"))]
Save, Save,
#[snafu(display("Failed to connect to network {} for interface: {}", id, iface))] Connect {
Connect { id: String, iface: String }, id: String,
iface: String,
#[snafu(display("Failed to start ap0 service: {}", source))]
StartAp0 { source: io::Error },
#[snafu(display("Failed to start wlan0 service: {}", source))]
StartWlan0 { source: io::Error },
#[snafu(display("JSON serialization failed: {}", source))]
SerdeSerialize { source: SerdeError },
#[snafu(display("Failed to open control interface for wpasupplicant"))]
WpaCtrlOpen {
#[snafu(source(from(failure::Error, std::convert::Into::into)))]
source: BoxError,
}, },
#[snafu(display("Request to wpasupplicant via wpactrl failed"))] StartAp0(io::Error),
WpaCtrlRequest {
#[snafu(source(from(failure::Error, std::convert::Into::into)))] StartWlan0(io::Error),
source: BoxError,
}, SerdeSerialize(SerdeError),
WpaCtrlOpen(failure::Error),
WpaCtrlRequest(failure::Error),
}
impl std::error::Error for NetworkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
NetworkError::Add { .. } => None,
NetworkError::NoState { .., ref source } => Some(source),
NetworkError::Disable { .. } => None,
NetworkError::Disconnect { .. } => None,
NetworkError::GenWpaPassphrase { .., ref source } => Some(source),
NetworkError::GenWpaPassphraseWarning { .. } => None,
NetworkError::Id { .. } => None,
NetworkError::NoIp { .., ref source } => Some(source),
NetworkError::Rssi { .. } => None,
NetworkError::RssiPercent { .. } => None,
NetworkError::Ssid { .. } => None,
NetworkError::State { .. } => None,
NetworkError::Status { .. } => None,
NetworkError::Traffic { .. } => None,
NetworkError::SavedNetworks => None,
NetworkError::AvailableNetworks { .. } => None,
NetworkError::MissingParams(ref source) => Some(source),
NetworkError::Modify { .. } => None,
NetworkError::Ip { .. } => None,
NetworkError::ParseString(ref source) => Some(source),
NetworkError::NoTraffic { .., ref source } => Some(source),
NetworkError::Reassociate { .. } => None,
NetworkError::Reconfigure { .. } => None,
NetworkError::Reconnect { .. } => None,
NetworkError::Regex(ref source) => Some(source),
NetworkError::Delete { .. } => None,
NetworkError::WpaState(ref source) => Some(source),
NetworkError::WpaOperstate(ref source) => Some(source),
NetworkError::Save => None,
NetworkError::Connect { .. } => None,
NetworkError::StartWlan0(ref source) => Some(source),
NetworkError::StartAp0(ref source) => Some(source),
NetworkError::SerdeSerialize(ref source) => Some(source),
NetworkError::WpaCtrlOpen(ref source) => Some(source),
NetworkError::WpaCtrlRequest(ref source) => Some(source),
}
}
}
impl std::fmt::Display for NetworkError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
NetworkError::Add { ref ssid } => {
write!(f, "Failed to add network for {}", ssid)
},
NetworkError::NoState { ref iface, .. } => {
write!(f, "Failed to retrieve state for interface: {}", iface)
},
NetworkError::Disable { ref id, ref iface } => {
write!(f, "Failed to disable network {} for interface: {}", id, iface)
},
NetworkError::Disconnect { ref iface } => {
write!(f, "Failed to disconnect {}", iface)
},
NetworkError::GenWpaPassphrase { ref ssid, .. } => {
write!(f, "Failed to generate wpa passphrase for {}", ssid)
},
NetworkError::GenWpaPassphraseWarning { ref ssid, ref err_msg } => {
write!(f, "Failed to generate wpa passphrase for {}: {}", ssid, err_msg)
},
NetworkError::Id { ref ssid, ref iface } => {
write!(f, "No ID found for {} on interface: {}: {}", ssid, iface)
},
NetworkError::NoIp { ref iface, .. } => {
write!(f, "Could not access IP address for interface: {}", iface)
},
NetworkError::Rssi { ref iface } => {
write!(f, "Could not find RSSI for interface: {}", iface)
},
NetworkError::RssiPercent { ref iface } => {
write!(f, "Could not find signal quality (%) for interface: {}", iface)
},
NetworkError::Ssid { ref iface } => {
write!(f, "Could not find SSID for interface: {}", iface)
},
NetworkError::State { ref iface } => {
write!(f, "No state found for interface: {}", iface)
},
NetworkError::Status { ref iface } => {
write!(f, "No status found for interface: {}", iface)
},
NetworkError::Traffic { ref iface } => {
write!(f, "Could not find network traffice for interface: {}", iface)
},
NetworkError::SavedNetworks => {
write!("No saved networks found for default interface")
},
NetworkError::AvailableNetworks { ref iface } => {
write!(f, "No networks found in range of interface: {}", iface)
},
NetworkError::MissingParams(ref source) => {
write!(f, "Missing expected parameters: {}", source)
},
NetworkError::Modify { ref id, ref iface } => {
write!(f, "Failed to set new password for network {} on {}", id, iface)
},
NetworkError::Ip { ref iface } => {
write!(f, "No IP found for interface: {}", iface)
},
NetworkError::ParseString(_) => {
write!("Failed to parse integer from string for RSSI value")
},
NetworkError::NoTraffic { ref id, .. } => {
write!("Failed to retrieve network traffic measurement for {}", id)
},
NetworkError::Reassociate { ref iface } => {
write!(f, "Failed to reassociate with WiFi network for interface: {}", iface)
},
NetworkError::Reconfigure => {
write!("Failed to force reread of wpa_supplicant configuration file")
},
NetworkError::Reconnect { ref iface } => {
write!(f, "Failed to reconnect with WiFi network for interface: {}", iface)
},
NetworkError::Regex(_) => {
write!("Regex command failed")
},
NetworkError::Delete { ref id, ref iface } => {
write!(f, "Failed to delete network {} for interface: {}", id, iface)
},
NetworkError::WpaState(_) => {
write!("Failed to retrieve state of wlan0 service")
},
NetworkError::WpaOperstate(_) => {
write!("Failed to retrieve connection state of wlan0 interface")
},
NetworkError::Save => {
write!("Failed to save configuration changes to file")
},
NetworkError::Connect { ref id, ref iface } => {
write!(f, "Failed to connect to network {} for interface: {}", id, iface)
},
NetworkError::StartWlan0(_) => {
write!("Failed to start ap0 service")
},
NetworkError::StartAp0(_) => {
write!("Failed to start wlan0 service")
},
NetworkError::SerdeSerialize(_) => {
write!("JSON serialization failed")
},
NetworkError::WpaCtrlOpen(_) => {
write!("Failed to open control interface for wpasupplicant")
},
NetworkError::WpaCtrlRequest(_) => {
write!("Request to wpasupplicant via wpactrl failed")
},
}
}
} }
impl From<NetworkError> for Error { impl From<NetworkError> for Error {
fn from(err: NetworkError) -> Self { fn from(err: NetworkError) -> Self {
match &err { match &err {
NetworkError::ActivateAp { err_msg } => Error {
code: ErrorCode::ServerError(-32015),
message: err_msg.to_string(),
data: None,
},
NetworkError::ActivateClient { err_msg } => Error {
code: ErrorCode::ServerError(-32017),
message: err_msg.to_string(),
data: None,
},
NetworkError::Add { ssid } => Error { NetworkError::Add { ssid } => Error {
code: ErrorCode::ServerError(-32000), code: ErrorCode::ServerError(-32000),
message: format!("Failed to add network for {}", ssid), message: format!("Failed to add network for {}", ssid),
@ -243,7 +386,7 @@ impl From<NetworkError> for Error {
message: format!("No networks found in range of {}", iface), message: format!("No networks found in range of {}", iface),
data: None, data: None,
}, },
NetworkError::MissingParams { e } => e.clone(), NetworkError::MissingParams(e) => e.clone(),
NetworkError::Modify { id, iface } => Error { NetworkError::Modify { id, iface } => Error {
code: ErrorCode::ServerError(-32033), code: ErrorCode::ServerError(-32033),
message: format!("Failed to set new password for network {} on {}", id, iface), message: format!("Failed to set new password for network {} on {}", id, iface),

View File

@ -317,7 +317,7 @@ pub fn run() -> Result<(), BoxError> {
Err(_) => Err(Error::from(NetworkError::Connect { id, iface })), Err(_) => Err(Error::from(NetworkError::Connect { id, iface })),
} }
} }
Err(e) => Err(Error::from(NetworkError::MissingParams { e })), Err(e) => Err(Error::from(NetworkError::MissingParams(e))),
} }
}); });

View File

@ -21,13 +21,9 @@ use std::{
str, str,
}; };
use crate::error::{ use crate::error::NetworkError;
GenWpaPassphrase, NetworkError, NoIp, NoState, NoTraffic, ParseString, SerdeSerialize,
StartAp0, StartWlan0, WlanState, WpaCtrlOpen, WpaCtrlRequest,
};
use probes::network; use probes::network;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use crate::utils; use crate::utils;
@ -122,6 +118,14 @@ pub struct WiFi {
pub pass: String, pub pass: String,
} }
// TODO: wrap this into a helper function:
//
// let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
// let mut wpa = wpactrl::WpaCtrl::new()
// .ctrl_path(wpa_path)
// .open()
// .map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
/* GET - Methods for retrieving data */ /* GET - Methods for retrieving data */
/// Retrieve list of available wireless access points for a given network /// Retrieve list of available wireless access points for a given network
@ -145,8 +149,9 @@ pub fn available_networks(iface: &str) -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new() let mut wpa = wpactrl::WpaCtrl::new()
.ctrl_path(wpa_path) .ctrl_path(wpa_path)
.open() .open()
.context(WpaCtrlOpen)?; .map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
wpa.request("SCAN").context(WpaCtrlRequest)?; wpa.request("SCAN")
.map_err(|source| NetworkError::WpaCtrlRequest(source.compat()))?;
let networks = wpa.request("SCAN_RESULTS").context(WpaCtrlRequest)?; let networks = wpa.request("SCAN_RESULTS").context(WpaCtrlRequest)?;
let mut scan = Vec::new(); let mut scan = Vec::new();
for network in networks.lines() { for network in networks.lines() {
@ -204,8 +209,10 @@ pub fn id(iface: &str, ssid: &str) -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new() let mut wpa = wpactrl::WpaCtrl::new()
.ctrl_path(wpa_path) .ctrl_path(wpa_path)
.open() .open()
.context(WpaCtrlOpen)?; .map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let networks = wpa.request("LIST_NETWORKS").context(WpaCtrlRequest)?; let networks = wpa
.request("LIST_NETWORKS")
.map_err(|source| NetworkError::WpaCtrlRequest(source.compat()))?;
let mut id = Vec::new(); let mut id = Vec::new();
for network in networks.lines() { for network in networks.lines() {
let v: Vec<&str> = network.split('\t').collect(); let v: Vec<&str> = network.split('\t').collect();
@ -239,7 +246,7 @@ pub fn id(iface: &str, ssid: &str) -> Result<Option<String>, NetworkError> {
/// ///
pub fn ip(iface: &str) -> Result<Option<String>, NetworkError> { pub fn ip(iface: &str) -> Result<Option<String>, NetworkError> {
let net_if: String = iface.to_string(); let net_if: String = iface.to_string();
let ifaces = get_if_addrs::get_if_addrs().context(NoIp { iface: net_if })?; let ifaces = get_if_addrs::get_if_addrs().map_err(|_| NetworkError::NoIp { iface: net_if })?;
let ip = ifaces let ip = ifaces
.iter() .iter()
.find(|&i| i.name == iface) .find(|&i| i.name == iface)
@ -268,7 +275,7 @@ pub fn rssi(iface: &str) -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new() let mut wpa = wpactrl::WpaCtrl::new()
.ctrl_path(wpa_path) .ctrl_path(wpa_path)
.open() .open()
.context(WpaCtrlOpen)?; .map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?; let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?;
let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?; let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?;
@ -300,7 +307,7 @@ pub fn rssi_percent(iface: &str) -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new() let mut wpa = wpactrl::WpaCtrl::new()
.ctrl_path(wpa_path) .ctrl_path(wpa_path)
.open() .open()
.context(WpaCtrlOpen)?; .map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?; let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?;
let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?; let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?;
@ -335,7 +342,9 @@ pub fn rssi_percent(iface: &str) -> Result<Option<String>, NetworkError> {
/// sent to the caller. /// sent to the caller.
/// ///
pub fn saved_networks() -> Result<Option<String>, NetworkError> { pub fn saved_networks() -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new().open().context(WpaCtrlOpen)?; let mut wpa = wpactrl::WpaCtrl::new()
.open()
.map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let networks = wpa.request("LIST_NETWORKS").context(WpaCtrlRequest)?; let networks = wpa.request("LIST_NETWORKS").context(WpaCtrlRequest)?;
let mut ssids = Vec::new(); let mut ssids = Vec::new();
for network in networks.lines() { for network in networks.lines() {