client: tighten some error types and apply helptext to io::Error

pull/121/head
Jake McGinty 2021-06-16 20:22:28 +09:00
parent 93b4b0b43c
commit 1aed782683
6 changed files with 75 additions and 43 deletions

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use shared::{ensure_dirs_exist, Cidr, IoErrorContext, Peer, WrappedIoError, CLIENT_DATA_DIR};
use std::{
fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
io::{self, Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
};
use wgctrl::InterfaceName;
@ -121,7 +121,7 @@ impl DataStore {
}
}
pub fn write(&mut self) -> Result<(), Error> {
pub fn write(&mut self) -> Result<(), io::Error> {
self.file.seek(SeekFrom::Start(0))?;
self.file.set_len(0)?;
self.file

View File

@ -245,7 +245,7 @@ fn update_hosts_file(
interface: &InterfaceName,
hosts_path: PathBuf,
peers: &[Peer],
) -> Result<(), Error> {
) -> Result<(), WrappedIoError> {
log::info!("updating {} with the latest peers.", "/etc/hosts".yellow());
let mut hosts_builder = HostsBuilder::new(format!("innernet {}", interface));
@ -358,7 +358,11 @@ fn redeem_invite(
network: NetworkOpt,
) -> Result<(), Error> {
log::info!("bringing up the interface.");
let resolved_endpoint = config.server.external_endpoint.resolve()?;
let resolved_endpoint = config
.server
.external_endpoint
.resolve()
.with_str(config.server.external_endpoint.to_string())?;
wg::up(
&iface,
&config.interface.private_key,
@ -370,7 +374,8 @@ fn redeem_invite(
resolved_endpoint,
)),
network,
)?;
)
.with_str(iface.to_string())?;
log::info!("Generating new keypair.");
let keypair = wgctrl::KeyPair::generate();
@ -397,7 +402,8 @@ fn redeem_invite(
log::info!("Changing keys and waiting for server's WireGuard interface to transition.",);
DeviceUpdate::new()
.set_private_key(keypair.private)
.apply(&iface, network.backend)?;
.apply(&iface, network.backend)
.with_str(iface.to_string())?;
thread::sleep(*REDEEM_TRANSITION_WAIT);
Ok(())
@ -442,7 +448,11 @@ fn fetch(
}
log::info!("bringing up the interface.");
let resolved_endpoint = config.server.external_endpoint.resolve()?;
let resolved_endpoint = config
.server
.external_endpoint
.resolve()
.with_str(config.server.external_endpoint.to_string())?;
wg::up(
interface,
&config.interface.private_key,
@ -454,7 +464,8 @@ fn fetch(
resolved_endpoint,
)),
network,
)?
)
.with_str(interface.to_string())?;
}
log::info!("fetching state from server.");
@ -521,7 +532,9 @@ fn fetch(
}
if device_config_changed {
device_config_builder.apply(&interface, network.backend)?;
device_config_builder
.apply(&interface, network.backend)
.with_str(interface.to_string())?;
if let Some(path) = hosts_path {
update_hosts_file(interface, path, &peers)?;
@ -534,7 +547,7 @@ fn fetch(
}
store.set_cidrs(cidrs);
store.update_peers(peers)?;
store.write()?;
store.write().with_str(interface.to_string())?;
Ok(())
}
@ -1020,6 +1033,9 @@ fn main() {
if let Some(e) = e.downcast_ref::<WrappedIoError>() {
util::permissions_helptext(e);
}
if let Some(e) = e.downcast_ref::<io::Error>() {
util::permissions_helptext(e);
}
std::process::exit(1);
}
}

View File

@ -102,7 +102,7 @@ pub fn human_size(bytes: u64) -> String {
}
}
pub fn permissions_helptext(e: &WrappedIoError) {
pub fn permissions_helptext(e: &io::Error) {
if e.raw_os_error() == Some(1) {
let current_exe = std::env::current_exe()
.ok()

View File

@ -128,7 +128,7 @@ impl InterfaceConfig {
.with_extension("conf")
}
fn build_config_file_path(interface: &InterfaceName) -> Result<PathBuf, Error> {
fn build_config_file_path(interface: &InterfaceName) -> Result<PathBuf, WrappedIoError> {
ensure_dirs_exist(&[*CLIENT_CONFIG_DIR])?;
Ok(Self::get_path(interface))
}

View File

@ -1,5 +1,3 @@
use crate::Error;
use anyhow::anyhow;
use ipnetwork::IpNetwork;
use netlink_packet_core::{
NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST,
@ -12,9 +10,12 @@ use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
use std::io;
use wgctrl::InterfaceName;
fn if_nametoindex(interface: &InterfaceName) -> Result<u32, Error> {
fn if_nametoindex(interface: &InterfaceName) -> Result<u32, io::Error> {
match unsafe { libc::if_nametoindex(interface.as_ptr()) } {
0 => Err(anyhow!("couldn't find interface '{}'.", interface)),
0 => Err(io::Error::new(
io::ErrorKind::NotFound,
format!("couldn't find interface '{}'.", interface),
)),
index => Ok(index),
}
}
@ -52,7 +53,7 @@ fn netlink_call(
Ok(response)
}
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), Error> {
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
let index = if_nametoindex(interface)?;
let message = LinkMessage {
header: LinkHeader {
@ -66,7 +67,7 @@ pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), Error> {
Ok(())
}
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), Error> {
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Error> {
let index = if_nametoindex(interface)?;
let (family, nlas) = match addr {
IpNetwork::V4(network) => {
@ -101,7 +102,7 @@ pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), Error>
Ok(())
}
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, Error> {
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io::Error> {
let if_index = if_nametoindex(interface)?;
let (address_family, dst) = match cidr {
IpNetwork::V4(network) => (AF_INET as u8, network.network().octets().to_vec()),

View File

@ -1,10 +1,13 @@
use crate::{Error, IoErrorContext, NetworkOpt};
use ipnetwork::IpNetwork;
use std::net::{IpAddr, SocketAddr};
use std::{
io,
net::{IpAddr, SocketAddr},
};
use wgctrl::{Backend, Device, DeviceUpdate, InterfaceName, PeerConfigBuilder};
#[cfg(target_os = "macos")]
fn cmd(bin: &str, args: &[&str]) -> Result<std::process::Output, Error> {
fn cmd(bin: &str, args: &[&str]) -> Result<std::process::Output, io::Error> {
let output = std::process::Command::new(bin).args(args).output()?;
log::debug!("cmd: {} {}", bin, args.join(" "));
log::debug!("status: {:?}", output.status.code());
@ -13,19 +16,21 @@ fn cmd(bin: &str, args: &[&str]) -> Result<std::process::Output, Error> {
if output.status.success() {
Ok(output)
} else {
Err(anyhow::anyhow!(
"failed to run {} {} command: {}",
bin,
args.join(" "),
String::from_utf8_lossy(&output.stderr)
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"failed to run {} {} command: {}",
bin,
args.join(" "),
String::from_utf8_lossy(&output.stderr)
),
))
}
}
#[cfg(target_os = "macos")]
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), Error> {
let real_interface =
wgctrl::backends::userspace::resolve_tun(interface).with_str(interface.to_string())?;
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Error> {
let real_interface = wgctrl::backends::userspace::resolve_tun(interface)?;
if addr.is_ipv4() {
cmd(
@ -49,9 +54,8 @@ pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), Error>
}
#[cfg(target_os = "macos")]
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), Error> {
let real_interface =
wgctrl::backends::userspace::resolve_tun(interface).with_str(interface.to_string())?;
pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
let real_interface = wgctrl::backends::userspace::resolve_tun(interface)?;
cmd("ifconfig", &[&real_interface, "mtu", &mtu.to_string()])?;
Ok(())
}
@ -69,11 +73,17 @@ pub fn up(
listen_port: Option<u16>,
peer: Option<(&str, IpAddr, SocketAddr)>,
network: NetworkOpt,
) -> Result<(), Error> {
) -> Result<(), io::Error> {
let mut device = DeviceUpdate::new();
if let Some((public_key, address, endpoint)) = peer {
let prefix = if address.is_ipv4() { 32 } else { 128 };
let peer_config = PeerConfigBuilder::new(&wgctrl::Key::from_base64(&public_key)?)
let peer_config =
PeerConfigBuilder::new(&wgctrl::Key::from_base64(&public_key).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"failed to parse base64 public key",
)
})?)
.add_allowed_ip(address, prefix)
.set_endpoint(endpoint);
device = device.add_peer(peer_config);
@ -85,7 +95,12 @@ pub fn up(
.set_private_key(wgctrl::Key::from_base64(&private_key).unwrap())
.apply(interface, network.backend)?;
set_addr(interface, address)?;
set_up(interface, network.mtu.unwrap_or_else(|| if address.is_ipv4() { 1420 } else { 1400 }))?;
set_up(
interface,
network
.mtu
.unwrap_or_else(|| if address.is_ipv4() { 1420 } else { 1400 }),
)?;
if !network.no_routing {
add_route(interface, address)?;
}
@ -119,9 +134,8 @@ pub fn down(interface: &InterfaceName, backend: Backend) -> Result<(), Error> {
/// Returns an error if the process doesn't exit successfully, otherwise returns
/// true if the route was changed, false if the route already exists.
#[cfg(target_os = "macos")]
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, Error> {
let real_interface =
wgctrl::backends::userspace::resolve_tun(interface).with_str(interface.to_string())?;
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io::Error> {
let real_interface = wgctrl::backends::userspace::resolve_tun(interface)?;
let output = cmd(
"route",
&[
@ -135,11 +149,12 @@ pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, Err
)?;
let stderr = String::from_utf8_lossy(&output.stderr);
if !output.status.success() {
Err(anyhow::anyhow!(
"failed to add route for device {} ({}): {}",
&interface,
real_interface,
stderr
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"failed to add route for device {} ({}): {}",
&interface, real_interface, stderr
),
))
} else {
Ok(!stderr.contains("File exists"))