wgctrl-rs(userspace): gracefully handle stale `.name` files on macOS

* wgctrl-rs(userspace): clean stale namefiles on starting up interface

Fixes #114

* wgctrl-rs(userspace): check connectability of interface socket in enumerate()
pull/127/head
Jake McGinty 2021-08-02 22:12:29 +09:00 committed by GitHub
parent bbfb11e175
commit 40e8ca68f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 30 additions and 26 deletions

View File

@ -3,14 +3,7 @@ use crate::{Backend, Device, DeviceUpdate, InterfaceName, PeerConfig, PeerInfo,
#[cfg(target_os = "linux")]
use crate::Key;
use std::{
fs,
io::{self, prelude::*, BufReader},
os::unix::net::UnixStream,
path::{Path, PathBuf},
process::Command,
time::{Duration, SystemTime},
};
use std::{fs, io::{self, prelude::*, BufReader}, os::unix::net::UnixStream, path::{Path, PathBuf}, process::{Command, Output}, time::{Duration, SystemTime}};
static VAR_RUN_PATH: &str = "/var/run/wireguard";
static RUN_PATH: &str = "/run/wireguard";
@ -66,9 +59,12 @@ pub fn enumerate() -> Result<Vec<InterfaceName>, io::Error> {
for entry in fs::read_dir(get_base_folder()?)? {
let path = entry?.path();
if path.extension() == Some(OsStr::new("name")) {
let stem = path.file_stem().map(|stem| stem.to_str()).flatten();
if let Some(name) = stem {
interfaces.push(name.parse()?);
let stem = path.file_stem()
.and_then(|stem| stem.to_str())
.and_then(|name| name.parse::<InterfaceName>().ok())
.filter(|iface| open_socket(iface).is_ok());
if let Some(iface) = stem {
interfaces.push(iface);
}
}
}
@ -271,26 +267,34 @@ fn get_userspace_implementation() -> String {
.unwrap_or_else(|_| "wireguard-go".to_string())
}
fn start_userspace_wireguard(iface: &InterfaceName) -> io::Result<Output> {
let mut command = Command::new(&get_userspace_implementation());
let output = if cfg!(target_os = "linux") {
command.args(&[iface.to_string()]).output()?
} else {
command
.env(
"WG_TUN_NAME_FILE",
&format!("{}/{}.name", VAR_RUN_PATH, iface),
)
.args(&["utun"])
.output()?
};
if !output.status.success() {
Err(io::ErrorKind::AddrNotAvailable.into())
} else {
Ok(output)
}
}
pub fn apply(builder: &DeviceUpdate, iface: &InterfaceName) -> io::Result<()> {
// If we can't open a configuration socket to an existing interface, try starting it.
let mut sock = match open_socket(iface) {
Err(_) => {
fs::create_dir_all(VAR_RUN_PATH)?;
let mut command = Command::new(&get_userspace_implementation());
let output = if cfg!(target_os = "linux") {
command.args(&[iface.to_string()]).output()?
} else {
command
.env(
"WG_TUN_NAME_FILE",
&format!("{}/{}.name", VAR_RUN_PATH, iface),
)
.args(&["utun"])
.output()?
};
if !output.status.success() {
return Err(io::ErrorKind::AddrNotAvailable.into());
}
// Clear out any old namefiles if they didn't lead to a connected socket.
let _ = fs::remove_file(get_namefile(iface)?);
start_userspace_wireguard(iface)?;
std::thread::sleep(Duration::from_millis(100));
open_socket(iface)
.map_err(|e| io::Error::new(e.kind(), format!("failed to open socket ({})", e)))?