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",
"serde 1.0.130",
"serde_json",
"snafu 0.6.10",
"wpactrl",
]

View File

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

View File

@ -1,150 +1,293 @@
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 serde_json::error::Error as SerdeError;
use snafu::Snafu;
pub type BoxError = Box<dyn error::Error>;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
#[derive(Debug)]
pub enum NetworkError {
#[snafu(display("{}", err_msg))]
ActivateAp { err_msg: String },
Add {
ssid: String,
},
#[snafu(display("{}", err_msg))]
ActivateClient { err_msg: String },
NoState {
iface: String,
source: io::Error,
},
#[snafu(display("Failed to add network for {}", ssid))]
Add { ssid: String },
Disable {
id: String,
iface: String,
},
#[snafu(display("Failed to retrieve state for interface: {}", iface))]
NoState { iface: String, source: io::Error },
Disconnect {
iface: String,
},
#[snafu(display("Failed to disable network {} for interface: {}", id, iface))]
Disable { id: String, iface: String },
GenWpaPassphrase {
ssid: String,
source: io::Error,
},
#[snafu(display("Failed to disconnect {}", iface))]
Disconnect { iface: String },
GenWpaPassphraseWarning {
ssid: String,
err_msg: String,
},
#[snafu(display("Failed to generate wpa passphrase for {}: {}", ssid, source))]
GenWpaPassphrase { ssid: String, source: io::Error },
Id {
ssid: String,
iface: String,
},
#[snafu(display("Failed to generate wpa passphrase for {}: {}", ssid, err_msg))]
GenWpaPassphraseWarning { ssid: String, err_msg: String },
NoIp {
iface: String,
source: io::Error,
},
#[snafu(display("No ID found for {} on interface: {}", ssid, iface))]
Id { ssid: String, iface: String },
Rssi {
iface: String,
},
#[snafu(display("Could not access IP address for interface: {}", iface))]
NoIp { iface: String, source: io::Error },
RssiPercent {
iface: String,
},
#[snafu(display("Could not find RSSI for interface: {}", iface))]
Rssi { iface: String },
Ssid {
iface: String,
},
#[snafu(display("Could not find signal quality (%) for interface: {}", iface))]
RssiPercent { iface: String },
State {
iface: String,
},
#[snafu(display("Could not find SSID for interface: {}", iface))]
Ssid { iface: String },
Status {
iface: String,
},
#[snafu(display("No state found for interface: {}", iface))]
State { iface: String },
Traffic {
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,
#[snafu(display("No networks found in range of interface: {}", iface))]
AvailableNetworks { iface: String },
AvailableNetworks {
iface: String,
},
#[snafu(display("Missing expected parameters: {}", e))]
MissingParams { e: Error },
MissingParams(JsonRpcError),
#[snafu(display("Failed to set new password for network {} on {}", id, iface))]
Modify { id: String, iface: String },
Modify {
id: String,
iface: String,
},
#[snafu(display("No IP found for interface: {}", iface))]
Ip { iface: String },
Ip {
iface: String,
},
#[snafu(display("Failed to parse integer from string for RSSI value: {}", source))]
ParseString { source: std::num::ParseIntError },
ParseString(std::num::ParseIntError),
#[snafu(display(
"Failed to retrieve network traffic measurement for {}: {}",
iface,
source
))]
NoTraffic { iface: String, source: ProbeError },
NoTraffic {
iface: String,
source: ProbeError,
},
#[snafu(display("Failed to reassociate with WiFi network for interface: {}", iface))]
Reassociate { iface: String },
Reassociate {
iface: String,
},
#[snafu(display("Failed to force reread of wpa_supplicant configuration file"))]
Reconfigure,
#[snafu(display("Failed to reconnect with WiFi network for interface: {}", iface))]
Reconnect { iface: String },
Reconnect {
iface: String,
},
#[snafu(display("Regex command failed"))]
Regex { source: regex::Error },
Regex(regex::Error),
#[snafu(display("Failed to delete network {} for interface: {}", id, iface))]
Delete { id: String, iface: String },
Delete {
id: String,
iface: String,
},
#[snafu(display("Failed to retrieve state of wlan0 service: {}", source))]
WlanState { source: io::Error },
WlanState(io::Error),
#[snafu(display("Failed to retrieve connection state of wlan0 interface: {}", source))]
WlanOperstate { source: io::Error },
WlanOperstate(io::Error),
#[snafu(display("Failed to save configuration changes to file"))]
Save,
#[snafu(display("Failed to connect to network {} for interface: {}", id, iface))]
Connect { 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,
Connect {
id: String,
iface: String,
},
#[snafu(display("Request to wpasupplicant via wpactrl failed"))]
WpaCtrlRequest {
#[snafu(source(from(failure::Error, std::convert::Into::into)))]
source: BoxError,
},
StartAp0(io::Error),
StartWlan0(io::Error),
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 {
fn from(err: NetworkError) -> Self {
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 {
code: ErrorCode::ServerError(-32000),
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),
data: None,
},
NetworkError::MissingParams { e } => e.clone(),
NetworkError::MissingParams(e) => e.clone(),
NetworkError::Modify { id, iface } => Error {
code: ErrorCode::ServerError(-32033),
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(e) => Err(Error::from(NetworkError::MissingParams { e })),
Err(e) => Err(Error::from(NetworkError::MissingParams(e))),
}
});

View File

@ -21,13 +21,9 @@ use std::{
str,
};
use crate::error::{
GenWpaPassphrase, NetworkError, NoIp, NoState, NoTraffic, ParseString, SerdeSerialize,
StartAp0, StartWlan0, WlanState, WpaCtrlOpen, WpaCtrlRequest,
};
use crate::error::NetworkError;
use probes::network;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use crate::utils;
@ -122,6 +118,14 @@ pub struct WiFi {
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 */
/// 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()
.ctrl_path(wpa_path)
.open()
.context(WpaCtrlOpen)?;
wpa.request("SCAN").context(WpaCtrlRequest)?;
.map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
wpa.request("SCAN")
.map_err(|source| NetworkError::WpaCtrlRequest(source.compat()))?;
let networks = wpa.request("SCAN_RESULTS").context(WpaCtrlRequest)?;
let mut scan = Vec::new();
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()
.ctrl_path(wpa_path)
.open()
.context(WpaCtrlOpen)?;
let networks = wpa.request("LIST_NETWORKS").context(WpaCtrlRequest)?;
.map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let networks = wpa
.request("LIST_NETWORKS")
.map_err(|source| NetworkError::WpaCtrlRequest(source.compat()))?;
let mut id = Vec::new();
for network in networks.lines() {
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> {
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
.iter()
.find(|&i| i.name == iface)
@ -268,7 +275,7 @@ pub fn rssi(iface: &str) -> Result<Option<String>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::new()
.ctrl_path(wpa_path)
.open()
.context(WpaCtrlOpen)?;
.map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?;
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()
.ctrl_path(wpa_path)
.open()
.context(WpaCtrlOpen)?;
.map_err(|source| NetworkError::WpaCtrlOpen(source.compat()))?;
let status = wpa.request("SIGNAL_POLL").context(WpaCtrlRequest)?;
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.
///
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 mut ssids = Vec::new();
for network in networks.lines() {