From 5b744d1f78b5b275d058088dddbdb7f07c1b4269 Mon Sep 17 00:00:00 2001
From: Jake McGinty <me@jake.su>
Date: Thu, 20 May 2021 03:16:19 +0900
Subject: [PATCH] client, wgctrl: fix various linux userspace issues

Fixes #75
---
 client/src/main.rs                  |  4 +++-
 server/src/main.rs                  |  6 +++++-
 shared/src/wg.rs                    | 18 ++++++++++++++--
 wgctrl-rs/src/backends/userspace.rs | 33 ++++++++++++++++++++---------
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/client/src/main.rs b/client/src/main.rs
index c212d84..c039b80 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -253,6 +253,7 @@ fn install(
 
     let iface = iface.parse()?;
     redeem_invite(&iface, config, target_conf, network).map_err(|e| {
+        println!("{} failed to start the interface: {}.", "[!]".red(), e);
         println!("{} bringing down the interface.", "[*]".dimmed());
         if let Err(e) = wg::down(&iface, network.backend) {
             println!("{} failed to bring down interface: {}.", "[*]".yellow(), e.to_string());
@@ -263,10 +264,11 @@ fn install(
 
     let mut fetch_success = false;
     for _ in 0..3 {
-        if fetch(&iface, false, hosts_file.clone(), network).is_ok() {
+        if fetch(&iface, true, hosts_file.clone(), network).is_ok() {
             fetch_success = true;
             break;
         }
+        thread::sleep(Duration::from_secs(1));
     }
     if !fetch_success {
         println!(
diff --git a/server/src/main.rs b/server/src/main.rs
index 8d5267a..5b56dcf 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -380,8 +380,11 @@ fn spawn_expired_invite_sweeper(db: Db) {
         loop {
             interval.tick().await;
             match DatabasePeer::delete_expired_invites(&db.lock()) {
-                Ok(deleted) => log::info!("Deleted {} expired peer invitations.", deleted),
+                Ok(deleted) if deleted > 0 => {
+                    log::info!("Deleted {} expired peer invitations.", deleted)
+                },
                 Err(e) => log::error!("Failed to delete expired peer invitations: {}", e),
+                _ => {},
             }
         }
     });
@@ -439,6 +442,7 @@ async fn serve(
         let context = context.clone();
         async move {
             Ok::<_, http::Error>(hyper::service::service_fn(move |req: Request<Body>| {
+                log::debug!("{} - {} {}", &remote_addr, req.method(), req.uri());
                 hyper_service(req, context.clone(), remote_addr)
             }))
         }
diff --git a/shared/src/wg.rs b/shared/src/wg.rs
index fb8eca3..383ac79 100644
--- a/shared/src/wg.rs
+++ b/shared/src/wg.rs
@@ -8,6 +8,16 @@ use wgctrl::{Backend, Device, DeviceUpdate, InterfaceName, PeerConfigBuilder};
 
 fn cmd(bin: &str, args: &[&str]) -> Result<process::Output, Error> {
     let output = Command::new(bin).args(args).output()?;
+    log::debug!("{} {}", bin, args.join(" "));
+    log::debug!("status code {:?}", output.status.code());
+    log::trace!(
+        "stdout: {}",
+        String::from_utf8_lossy(&output.stdout)
+    );
+    log::trace!(
+        "stderr: {}",
+        String::from_utf8_lossy(&output.stderr)
+    );
     if output.status.success() {
         Ok(output)
     } else {
@@ -87,6 +97,7 @@ pub fn up(
     if !network.no_routing {
         add_route(interface, address)?;
     }
+    cmd("ip", &["link"])?;
     Ok(())
 }
 
@@ -107,7 +118,10 @@ pub fn set_listen_port(
 }
 
 pub fn down(interface: &InterfaceName, backend: Backend) -> Result<(), Error> {
-    Ok(Device::get(interface, backend)?.delete()?)
+    Ok(Device::get(interface, backend)
+        .with_str(interface.as_str_lossy())?
+        .delete()
+        .with_str(interface.as_str_lossy())?)
 }
 
 /// Add a route in the OS's routing table to get traffic flowing through this interface.
@@ -145,7 +159,7 @@ pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, Err
             &[
                 "route",
                 "add",
-                &cidr.to_string(),
+                &IpNetwork::new(cidr.network(), cidr.prefix())?.to_string(),
                 "dev",
                 &interface.to_string(),
             ],
diff --git a/wgctrl-rs/src/backends/userspace.rs b/wgctrl-rs/src/backends/userspace.rs
index 6b321d5..2b3768c 100644
--- a/wgctrl-rs/src/backends/userspace.rs
+++ b/wgctrl-rs/src/backends/userspace.rs
@@ -33,7 +33,11 @@ fn get_namefile(name: &InterfaceName) -> io::Result<PathBuf> {
 }
 
 fn get_socketfile(name: &InterfaceName) -> io::Result<PathBuf> {
-    Ok(get_base_folder()?.join(&format!("{}.sock", resolve_tun(name)?)))
+    if cfg!(target_os = "linux") {
+        Ok(get_base_folder()?.join(&format!("{}.sock", name)))
+    } else {
+        Ok(get_base_folder()?.join(&format!("{}.sock", resolve_tun(name)?)))
+    }
 }
 
 fn open_socket(name: &InterfaceName) -> io::Result<UnixStream> {
@@ -42,7 +46,10 @@ fn open_socket(name: &InterfaceName) -> io::Result<UnixStream> {
 
 pub fn resolve_tun(name: &InterfaceName) -> io::Result<String> {
     let namefile = get_namefile(name)?;
-    Ok(fs::read_to_string(namefile)?.trim().to_string())
+    Ok(fs::read_to_string(namefile)
+        .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "WireGuard name file can't be read"))?
+        .trim()
+        .to_string())
 }
 
 pub fn delete_interface(name: &InterfaceName) -> io::Result<()> {
@@ -269,18 +276,24 @@ pub fn apply(builder: &DeviceUpdate, iface: &InterfaceName) -> io::Result<()> {
     let mut sock = match open_socket(iface) {
         Err(_) => {
             fs::create_dir_all(VAR_RUN_PATH)?;
-            let output = Command::new(&get_userspace_implementation())
-                .env(
-                    "WG_TUN_NAME_FILE",
-                    &format!("{}/{}.name", VAR_RUN_PATH, iface),
-                )
-                .args(&["utun"])
-                .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() {
                 return Err(io::ErrorKind::AddrNotAvailable.into());
             }
             std::thread::sleep(Duration::from_millis(100));
-            open_socket(iface)?
+            open_socket(iface)
+                .map_err(|e| io::Error::new(e.kind(), format!("failed to open socket ({})", e)))?
         },
         Ok(sock) => sock,
     };