Repository: tonarino/innernet (language: rust)

Issue:
innernet-server: also report candidates like clients do



Discussion / hints:

Fix patch:
diff --git a/client/src/main.rs b/client/src/main.rs
index 15f48800..2d2884aa 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -4,6 +4,7 @@ use dialoguer::{Confirm, Input};
 use hostsfile::HostsBuilder;
 use indoc::eprintdoc;
 use shared::{
+    get_local_addrs,
     interface_config::InterfaceConfig,
     prompts,
     wg::{DeviceExt, PeerInfoExt},
@@ -448,7 +449,7 @@ fn redeem_invite(
         target_conf.to_string_lossy().yellow()
     );
 
-    log::info!("Changing keys and waiting for server's WireGuard interface to transition.",);
+    log::info!("Changing keys and waiting 5s for server's WireGuard interface to transition.",);
     DeviceUpdate::new()
         .set_private_key(keypair.private)
         .apply(iface, network.backend)
@@ -550,10 +551,8 @@ fn fetch(
     store.update_peers(&peers)?;
     store.write().with_str(interface.to_string())?;
 
-    let candidates = wg::get_local_addrs()?
-        .into_iter()
+    let candidates: Vec<Endpoint> = get_local_addrs()?
         .map(|addr| SocketAddr::from((addr, device.listen_port.unwrap_or(51820))).into())
-        .take(10)
         .collect::<Vec<Endpoint>>();
     log::info!(
         "reporting {} interface address{} as NAT traversal candidates...",
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index ffedd76d..99509b87 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -106,6 +106,8 @@ mod handlers {
             // This might be avoidable if we were able to run code after we were certain the response
             // had flushed over the TCP socket, but that isn't easily accessible from this high-level
             // web framework.
+            //
+            // Related: https://github.com/hyperium/hyper/issues/2181
             tokio::task::spawn(async move {
                 tokio::time::sleep(*REDEEM_TRANSITION_WAIT).await;
                 log::info!(
diff --git a/server/src/main.rs b/server/src/main.rs
index 065d4a30..4ae602b4 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -8,8 +8,8 @@ use parking_lot::{Mutex, RwLock};
 use rusqlite::Connection;
 use serde::{Deserialize, Serialize};
 use shared::{
-    AddCidrOpts, AddPeerOpts, DeleteCidrOpts, IoErrorContext, NetworkOpt, RenamePeerOpts,
-    INNERNET_PUBKEY_HEADER,
+    get_local_addrs, AddCidrOpts, AddPeerOpts, DeleteCidrOpts, Endpoint, IoErrorContext,
+    NetworkOpt, PeerContents, RenamePeerOpts, INNERNET_PUBKEY_HEADER,
 };
 use std::{
     collections::{HashMap, VecDeque},
@@ -479,7 +479,7 @@ async fn serve(
     log::debug!("opening database connection...");
     let conn = open_database_connection(&interface, conf)?;
 
-    let peers = DatabasePeer::list(&conn)?;
+    let mut peers = DatabasePeer::list(&conn)?;
     log::debug!("peers listed...");
     let peer_configs = peers
         .iter()
@@ -502,6 +502,27 @@ async fn serve(
 
     log::info!("{} peers added to wireguard interface.", peers.len());
 
+    let candidates: Vec<Endpoint> = get_local_addrs()?
+        .map(|addr| SocketAddr::from((addr, config.listen_port)).into())
+        .collect();
+    let num_candidates = candidates.len();
+    let myself = peers
+        .iter_mut()
+        .find(|peer| peer.ip == config.address)
+        .expect("Couldn't find server peer in peer list.");
+    myself.update(
+        &conn,
+        PeerContents {
+            candidates,
+            ..myself.contents.clone()
+        },
+    )?;
+
+    log::info!(
+        "{} local candidates added to server peer config.",
+        num_candidates
+    );
+
     let public_key = wgctrl::Key::from_base64(&config.private_key)?.generate_public();
     let db = Arc::new(Mutex::new(conn));
     let endpoints = spawn_endpoint_refresher(interface, network);
diff --git a/shared/src/lib.rs b/shared/src/lib.rs
index b399ab64..05cda3c2 100644
--- a/shared/src/lib.rs
+++ b/shared/src/lib.rs
@@ -79,3 +79,31 @@ pub fn chmod(file: &File, new_mode: u32) -> Result<bool, io::Error> {
 
     Ok(updated)
 }
+
+#[cfg(target_os = "macos")]
+pub fn _get_local_addrs() -> Result<impl Iterator<Item = std::net::IpAddr>, io::Error> {
+    use nix::{net::if_::InterfaceFlags, sys::socket::SockAddr};
+
+    let addrs = nix::ifaddrs::getifaddrs()?
+        .filter(|addr| {
+            addr.flags.contains(InterfaceFlags::IFF_UP)
+                && !addr.flags.intersects(
+                    InterfaceFlags::IFF_LOOPBACK
+                        | InterfaceFlags::IFF_POINTOPOINT
+                        | InterfaceFlags::IFF_PROMISC,
+                )
+        })
+        .filter_map(|addr| match addr.address {
+            Some(SockAddr::Inet(addr)) if addr.to_std().is_ipv4() => Some(addr.to_std().ip()),
+            _ => None,
+        });
+
+    Ok(addrs)
+}
+
+#[cfg(target_os = "linux")]
+pub use netlink::get_local_addrs as _get_local_addrs;
+
+pub fn get_local_addrs() -> Result<impl Iterator<Item = std::net::IpAddr>, io::Error> {
+    Ok(_get_local_addrs()?.take(10))
+}
diff --git a/shared/src/netlink.rs b/shared/src/netlink.rs
index 95a268b2..d85d0116 100644
--- a/shared/src/netlink.rs
+++ b/shared/src/netlink.rs
@@ -180,7 +180,7 @@ fn get_links() -> Result<Vec<String>, io::Error> {
     Ok(links)
 }
 
-pub fn get_local_addrs() -> Result<Vec<IpAddr>, io::Error> {
+pub fn get_local_addrs() -> Result<impl Iterator<Item = IpAddr>, io::Error> {
     let links = get_links()?;
     let addr_responses = netlink_call(
         RtnlMessage::GetAddress(AddressMessage::default()),
@@ -203,7 +203,7 @@ pub fn get_local_addrs() -> Result<Vec<IpAddr>, io::Error> {
                 None
             })
         // Only select addresses for helpful links
-        .filter(|nlas| nlas.iter().any(|nla| matches!(nla, address::nlas::Nla::Label(label) if links.contains(label))))
+        .filter(move |nlas| nlas.iter().any(|nla| matches!(nla, address::nlas::Nla::Label(label) if links.contains(label))))
         .filter_map(|nlas| nlas.iter().find_map(|nla| match nla {
             address::nlas::Nla::Address(name) if name.len() == 4 => {
                 let mut addr = [0u8; 4];
@@ -216,8 +216,7 @@ pub fn get_local_addrs() -> Result<Vec<IpAddr>, io::Error> {
                 Some(IpAddr::V6(addr.into()))
             },
             _ => None,
-        }))
-        .collect::<Vec<_>>();
+        }));
     Ok(addrs)
 }
 
@@ -228,6 +227,6 @@ mod tests {
     #[test]
     fn test_local_addrs() {
         let addrs = get_local_addrs().unwrap();
-        println!("{:?}", addrs);
+        println!("{:?}", addrs.collect::<Vec<_>>());
     }
 }
diff --git a/shared/src/wg.rs b/shared/src/wg.rs
index 5d4dca44..e44367ed 100644
--- a/shared/src/wg.rs
+++ b/shared/src/wg.rs
@@ -166,31 +166,6 @@ pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io:
 #[cfg(target_os = "linux")]
 pub use super::netlink::add_route;
 
-#[cfg(target_os = "macos")]
-pub fn get_local_addrs() -> Result<Vec<IpAddr>, io::Error> {
-    use nix::{net::if_::InterfaceFlags, sys::socket::SockAddr};
-
-    let addrs = nix::ifaddrs::getifaddrs()?
-        .filter(|addr| {
-            addr.flags.contains(InterfaceFlags::IFF_UP)
-                && !addr.flags.intersects(
-                    InterfaceFlags::IFF_LOOPBACK
-                        | InterfaceFlags::IFF_POINTOPOINT
-                        | InterfaceFlags::IFF_PROMISC,
-                )
-        })
-        .filter_map(|addr| match addr.address {
-            Some(SockAddr::Inet(addr)) if addr.to_std().is_ipv4() => Some(addr.to_std().ip()),
-            _ => None,
-        })
-        .collect::<Vec<_>>();
-
-    Ok(addrs)
-}
-
-#[cfg(target_os = "linux")]
-pub use super::netlink::get_local_addrs;
-
 pub trait DeviceExt {
     /// Diff the output of a wgctrl device with a list of server-reported peers.
     fn diff<'a>(&'a self, peers: &'a [Peer]) -> Vec<PeerDiff<'a>>;


Test patch: