From 7e0f7be95c6df7636c07d9445d477422420d78b5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 12 Jul 2022 22:34:05 +0800 Subject: [PATCH 01/10] lan_discovery_WOL: mid commit Signed-off-by: fufesou --- Cargo.lock | 83 +++++++++++++++ Cargo.toml | 1 + src/rendezvous_mediator.rs | 209 ++++++++++++++++++++++++++++++------- src/ui/ab.tis | 4 +- 4 files changed, 256 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb5c4cd12..e581e842e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1176,6 +1176,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "default-net" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e70d471b0ba4e722c85651b3bb04b6880dfdb1224a43ade80c1295314db646" +dependencies = [ + "libc", + "memalloc", + "system-configuration", + "windows", +] + [[package]] name = "deflate" version = "0.8.6" @@ -2673,6 +2685,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "memchr" version = "2.5.0" @@ -4080,6 +4098,7 @@ dependencies = [ "cpal", "ctrlc", "dasp", + "default-net", "dispatch", "enigo", "flexi_logger", @@ -4680,6 +4699,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation 0.9.3", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + [[package]] name = "system-deps" version = "1.3.2" @@ -5450,6 +5490,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +dependencies = [ + "windows_aarch64_msvc 0.30.0", + "windows_i686_gnu 0.30.0", + "windows_i686_msvc 0.30.0", + "windows_x86_64_gnu 0.30.0", + "windows_x86_64_msvc 0.30.0", +] + [[package]] name = "windows-service" version = "0.4.0" @@ -5494,6 +5547,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -5506,6 +5565,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +[[package]] +name = "windows_i686_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -5518,6 +5583,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +[[package]] +name = "windows_i686_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -5530,6 +5601,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +[[package]] +name = "windows_x86_64_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -5542,6 +5619,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +[[package]] +name = "windows_x86_64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" diff --git a/Cargo.toml b/Cargo.toml index ff17ac858..14cae5f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ rpassword = "6.0" base64 = "0.13" sysinfo = "0.23" num_cpus = "1.13" +default-net = "0.11.0" [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 28f2dbc3f..42b8c49f2 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -1,4 +1,7 @@ -use crate::server::{check_zombie, new as new_server, ServerPtr}; +use crate::{ + ipc::get_id, + server::{check_zombie, new as new_server, ServerPtr}, +}; use hbb_common::{ allow_err, anyhow::bail, @@ -15,8 +18,9 @@ use hbb_common::{ udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; +use serde_derive::{Deserialize, Serialize}; use std::{ - net::SocketAddr, + net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -62,11 +66,9 @@ impl RendezvousMediator { direct_server(server_cloned).await; }); #[cfg(not(any(target_os = "android", target_os = "ios")))] - if crate::platform::is_installed() { - std::thread::spawn(move || { - allow_err!(lan_discovery()); - }); - } + std::thread::spawn(move || { + allow_err!(lan_discovery()); + }); loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { @@ -546,9 +548,19 @@ pub fn get_broadcast_port() -> u16 { (RENDEZVOUS_PORT + 3) as _ } -pub fn get_mac() -> String { +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct DiscoveryPeer { + id: String, + mac: String, + ip: String, + username: String, + hostname: String, + platform: String, +} + +pub fn get_mac(ip: &IpAddr) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(Some(mac)) = mac_address::get_mac_address() { + if let Ok(mac) = get_mac_by_ip(ip) { mac.to_string() } else { "".to_owned() @@ -557,6 +569,56 @@ pub fn get_mac() -> String { "".to_owned() } +fn get_all_ipv4s() -> ResultType> { + let mut ipv4s = Vec::new(); + for interface in default_net::get_interfaces() { + for ipv4 in &interface.ipv4 { + ipv4s.push(ipv4.addr.clone()); + } + } + Ok(ipv4s) +} + +fn get_mac_by_ip(ip: &IpAddr) -> ResultType { + for interface in default_net::get_interfaces() { + match ip { + IpAddr::V4(local_ipv4) => { + if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + IpAddr::V6(local_ipv6) => { + if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + } + } + bail!("No interface found for ip: {:?}", ip); +} + +// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 +pub fn get_ipaddr_by_peer(peer: A) -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect(peer) { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip()), + Err(_) => return None, + }; +} + fn lan_discovery() -> ResultType<()> { let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); let socket = std::net::UdpSocket::bind(addr)?; @@ -569,18 +631,20 @@ fn lan_discovery() -> ResultType<()> { match msg_in.union { Some(rendezvous_message::Union::PeerDiscovery(p)) => { if p.cmd == "ping" { - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "pong".to_owned(), - mac: get_mac(), - id: Config::get_id(), - hostname: whoami::hostname(), - username: crate::platform::get_active_username(), - platform: whoami::platform().to_string(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "pong".to_owned(), + mac: get_mac(&self_addr), + id: Config::get_id(), + hostname: whoami::hostname(), + username: crate::platform::get_active_username(), + platform: whoami::platform().to_string(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + } } } _ => {} @@ -590,10 +654,25 @@ fn lan_discovery() -> ResultType<()> { } } -pub fn discover() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], 0)); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_broadcast(true)?; +fn create_broadcast_sockets() -> ResultType> { + let mut sockets = Vec::new(); + for v4_addr in get_all_ipv4s()? { + if v4_addr.is_private() { + let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; + s.set_broadcast(true)?; + log::debug!("Bind socket to {}", &v4_addr); + sockets.push(s) + } + } + Ok(sockets) +} + +fn send_query() -> ResultType> { + let sockets = create_broadcast_sockets()?; + if sockets.is_empty() { + bail!("Found no ipv4 addresses"); + } + let mut msg_out = Message::new(); let peer = PeerDiscovery { cmd: "ping".to_owned(), @@ -601,25 +680,49 @@ pub fn discover() -> ResultType<()> { }; msg_out.set_peer_discovery(peer); let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); - socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + for socket in &sockets { + socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + } log::info!("discover ping sent"); + Ok(sockets) +} + +fn wait_response( + socket: UdpSocket, + timeout: Option, +) -> ResultType> { let mut last_recv_time = Instant::now(); - let mut last_write_time = Instant::now(); - let mut last_write_n = 0; - // to-do: load saved peers, and update incrementally (then we can see offline) let mut peers = Vec::new(); - let mac = get_mac(); - socket.set_read_timeout(Some(std::time::Duration::from_millis(10)))?; + + socket.set_read_timeout(timeout)?; loop { let mut buf = [0; 2048]; - if let Ok((len, _)) = socket.recv_from(&mut buf) { + if let Ok((len, addr)) = socket.recv_from(&mut buf) { if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { match msg_in.union { Some(rendezvous_message::Union::PeerDiscovery(p)) => { last_recv_time = Instant::now(); if p.cmd == "pong" { - if p.mac != mac { - peers.push((p.id, p.username, p.hostname, p.platform)); + let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + get_mac(&self_addr) + } else { + "".to_owned() + }; + + let is_self = if !mac.is_empty() { + p.mac == mac + } else { + p.id == get_id() + }; + if !is_self { + peers.push(DiscoveryPeer { + id: p.id.clone(), + mac: p.mac.clone(), + ip: addr.to_string(), + username: p.username.clone(), + hostname: p.hostname.clone(), + platform: p.platform.clone(), + }); } } } @@ -627,15 +730,43 @@ pub fn discover() -> ResultType<()> { } } } - if last_write_time.elapsed().as_millis() > 300 && last_write_n != peers.len() { - config::LanPeers::store(serde_json::to_string(&peers)?); - last_write_time = Instant::now(); - last_write_n = peers.len(); - } if last_recv_time.elapsed().as_millis() > 3_000 { break; } } + Ok(peers) +} + +pub fn discover() -> ResultType<()> { + let sockets = send_query()?; + let mut join_handles = Vec::new(); + for socket in sockets { + let handle = std::thread::spawn(move || { + wait_response(socket, Some(std::time::Duration::from_millis(10))) + }); + join_handles.push(handle); + } + + // to-do: load saved peers, and update incrementally (then we can see offline) + let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) + { + Ok(p) => p, + _ => Vec::new(), + }; + + for handle in join_handles { + match handle.join() { + Ok(Ok(mut tmp)) => { + peers.append(&mut tmp); + } + Ok(Err(e)) => { + log::error!("Failed lan discove {e}"); + } + Err(e) => { + log::error!("Failed join lan discove thread {e:?}"); + } + } + } log::info!("discover ping done"); config::LanPeers::store(serde_json::to_string(&peers)?); Ok(()) diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 716ff1ba6..f2c8746d4 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -526,7 +526,7 @@ class MultipleSessions: Reactor.Component {
{translate('Recent Sessions')} {translate('Favorites')} - {handler.is_installed() && {translate('Discovered')}} + {{translate('Discovered')}} {translate('Address Book')}
{!this.hidden && } @@ -534,7 +534,7 @@ class MultipleSessions: Reactor.Component { {!this.hidden && ((type == "fav" && ) || - (type == "lan" && handler.is_installed() && ) || + (type == "lan" && ) || (type == "ab" && ) || )} ; From cbb34fb0210baa70ca3b5accb80d5b262e47bc78 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 11:18:53 +0800 Subject: [PATCH 02/10] lan_discovery_WOL: lan discovery almost done Signed-off-by: fufesou --- libs/hbb_common/src/udp.rs | 1 + src/rendezvous_mediator.rs | 122 +++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 38 deletions(-) diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index 3532dd1e0..c2d5057f2 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -37,6 +37,7 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result u16 { (RENDEZVOUS_PORT + 3) as _ } -#[derive(Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct DiscoveryPeer { id: String, - mac: String, - ip: String, + mac_ips: HashMap, username: String, hostname: String, platform: String, + online: bool, +} + +impl DiscoveryPeer { + fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { + self.id == other.id && self.username == other.username + } } pub fn get_mac(ip: &IpAddr) -> String { @@ -690,9 +695,9 @@ fn send_query() -> ResultType> { fn wait_response( socket: UdpSocket, timeout: Option, -) -> ResultType> { + tx: UnboundedSender, +) -> ResultType<()> { let mut last_recv_time = Instant::now(); - let mut peers = Vec::new(); socket.set_read_timeout(timeout)?; loop { @@ -709,20 +714,18 @@ fn wait_response( "".to_owned() }; - let is_self = if !mac.is_empty() { - p.mac == mac - } else { - p.id == get_id() - }; - if !is_self { - peers.push(DiscoveryPeer { + if mac != p.mac { + allow_err!(tx.send(DiscoveryPeer { id: p.id.clone(), - mac: p.mac.clone(), - ip: addr.to_string(), + mac_ips: HashMap::from([( + p.mac.clone(), + addr.ip().to_string() + )]), username: p.username.clone(), hostname: p.hostname.clone(), platform: p.platform.clone(), - }); + online: true, + })); } } } @@ -734,40 +737,83 @@ fn wait_response( break; } } - Ok(peers) + Ok(()) } -pub fn discover() -> ResultType<()> { - let sockets = send_query()?; - let mut join_handles = Vec::new(); +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { + let (tx, rx) = unbounded_channel::<_>(); for socket in sockets { - let handle = std::thread::spawn(move || { - wait_response(socket, Some(std::time::Duration::from_millis(10))) + let tx_clone = tx.clone(); + std::thread::spawn(move || { + allow_err!(wait_response( + socket, + Some(std::time::Duration::from_millis(10)), + tx_clone + )); }); - join_handles.push(handle); } + rx +} - // to-do: load saved peers, and update incrementally (then we can see offline) +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) { Ok(p) => p, _ => Vec::new(), }; + peers.iter_mut().for_each(|peer| { + peer.online = false; + }); - for handle in join_handles { - match handle.join() { - Ok(Ok(mut tmp)) => { - peers.append(&mut tmp); - } - Ok(Err(e)) => { - log::error!("Failed lan discove {e}"); - } - Err(e) => { - log::error!("Failed join lan discove thread {e:?}"); + // handle received peers + let mut response_set = HashSet::new(); + let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + loop { + tokio::select! { + data = rx.recv() => match data { + Some(peer) => { + let in_response_set = !response_set.insert(peer.id.clone()); + let mut pre_found = false; + for peer1 in &mut peers { + if peer1.is_same_peer(&peer) { + if in_response_set { + peer1.mac_ips.extend(peer.mac_ips.clone()); + peer1.hostname = peer.hostname.clone(); + peer1.platform = peer.platform.clone(); + peer1.online = true; + } else { + *peer1 = peer.clone(); + } + pre_found = true; + break + } + } + if !pre_found { + peers.push(peer); + } + + if last_write_time.elapsed().as_millis() > 300 { + config::LanPeers::store(serde_json::to_string(&peers)?); + last_write_time = Instant::now(); + } + } + None => { + break + } } } } - log::info!("discover ping done"); + config::LanPeers::store(serde_json::to_string(&peers)?); Ok(()) } + +#[tokio::main(flavor = "current_thread")] +pub async fn discover() -> ResultType<()> { + let sockets = send_query()?; + let rx = spawn_wait_responses(sockets); + handle_received_peers(rx).await?; + + log::info!("discover ping done"); + Ok(()) +} From 897d2b8e5703de6671fd853fe6f5f0ec7e2c69dd Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 18:06:19 +0800 Subject: [PATCH 03/10] lan_discovery_WOL: remove discovered peer Signed-off-by: fufesou --- Cargo.lock | 23 +++++++++++++++++++++++ libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/config.rs | 34 ++++++++++++++++++++++++++++++---- src/rendezvous_mediator.rs | 35 +++++++---------------------------- src/ui.rs | 9 ++++++++- src/ui/ab.tis | 5 ++++- 6 files changed, 73 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e581e842e..894382389 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2177,6 +2177,7 @@ dependencies = [ "serde 1.0.139", "serde_derive", "serde_json 1.0.82", + "serde_with", "socket2 0.3.19", "sodiumoxide", "tokio", @@ -4427,6 +4428,28 @@ dependencies = [ "serde 1.0.139", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde 1.0.139", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_yaml" version = "0.8.25" diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index acb93c78b..6fec67193 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -23,6 +23,7 @@ directories-next = "2.0" rand = "0.8" serde_derive = "1.0" serde = "1.0" +serde_with = "1.14.0" lazy_static = "1.4" confy = { git = "https://github.com/open-trade/confy" } dirs-next = "2.0" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index b4d8eaff3..ee8433770 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -856,10 +856,34 @@ impl LocalConfig { } } +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct DiscoveryPeer { + pub id: String, + #[serde(with = "serde_with::rust::map_as_tuple_list")] + pub mac_ips: HashMap, + pub username: String, + pub hostname: String, + pub platform: String, + pub online: bool, +} + +impl DiscoveryPeer { + pub fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { + self.id == other.id && self.username == other.username + } +} + #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LanPeers { - #[serde(default)] - pub peers: String, + // #[serde(default)] + // pub peers: String, + pub peers: Vec, +} + +#[derive(Serialize, Deserialize)] +struct MyConfig { + version: u8, + api_key: String, } impl LanPeers { @@ -874,8 +898,10 @@ impl LanPeers { } } - pub fn store(peers: String) { - let f = LanPeers { peers }; + pub fn store(peers: &Vec) { + let f = LanPeers { + peers: peers.clone(), + }; if let Err(err) = confy::store_path(Config::file_("_lan_peers"), f) { log::error!("Failed to store lan peers: {}", err); } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 9471ad4b7..d87357366 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -16,7 +16,6 @@ use hbb_common::{ udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; -use serde_derive::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, @@ -547,22 +546,6 @@ pub fn get_broadcast_port() -> u16 { (RENDEZVOUS_PORT + 3) as _ } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct DiscoveryPeer { - id: String, - mac_ips: HashMap, - username: String, - hostname: String, - platform: String, - online: bool, -} - -impl DiscoveryPeer { - fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { - self.id == other.id && self.username == other.username - } -} - pub fn get_mac(ip: &IpAddr) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Ok(mac) = get_mac_by_ip(ip) { @@ -695,7 +678,7 @@ fn send_query() -> ResultType> { fn wait_response( socket: UdpSocket, timeout: Option, - tx: UnboundedSender, + tx: UnboundedSender, ) -> ResultType<()> { let mut last_recv_time = Instant::now(); @@ -715,7 +698,7 @@ fn wait_response( }; if mac != p.mac { - allow_err!(tx.send(DiscoveryPeer { + allow_err!(tx.send(config::DiscoveryPeer { id: p.id.clone(), mac_ips: HashMap::from([( p.mac.clone(), @@ -740,7 +723,7 @@ fn wait_response( Ok(()) } -fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { let (tx, rx) = unbounded_channel::<_>(); for socket in sockets { let tx_clone = tx.clone(); @@ -755,12 +738,8 @@ fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver) -> ResultType<()> { - let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) - { - Ok(p) => p, - _ => Vec::new(), - }; +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { + let mut peers = config::LanPeers::load().peers; peers.iter_mut().for_each(|peer| { peer.online = false; }); @@ -793,7 +772,7 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) -> Resu } if last_write_time.elapsed().as_millis() > 300 { - config::LanPeers::store(serde_json::to_string(&peers)?); + config::LanPeers::store(&peers); last_write_time = Instant::now(); } } @@ -804,7 +783,7 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) -> Resu } } - config::LanPeers::store(serde_json::to_string(&peers)?); + config::LanPeers::store(&peers); Ok(()) } diff --git a/src/ui.rs b/src/ui.rs index a1f9093d0..4bf201c0e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -541,6 +541,12 @@ impl UI { PeerConfig::remove(&id); } + fn remove_discovered(&mut self, id: String) { + let mut peers = config::LanPeers::load().peers; + peers.retain(|x| x.id != id); + config::LanPeers::store(&peers); + } + fn new_remote(&mut self, id: String, remote_type: String) { let mut lock = self.0.lock().unwrap(); let args = vec![format!("--{}", remote_type), id.clone()]; @@ -690,7 +696,7 @@ impl UI { } fn get_lan_peers(&self) -> String { - config::LanPeers::load().peers + serde_json::to_string(&config::LanPeers::load().peers).unwrap_or_default() } fn get_uuid(&self) -> String { @@ -781,6 +787,7 @@ impl sciter::EventHandler for UI { fn get_size(); fn new_remote(String, bool); fn remove_peer(String); + fn remove_discovered(String); fn get_connect_status(); fn get_mouse_time(); fn check_mouse_time(); diff --git a/src/ui/ab.tis b/src/ui/ab.tis index f2c8746d4..03768c6da 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -320,7 +320,7 @@ class SessionList: Reactor.Component {
  • RDP
  • {translate('Rename')}
  • - {this.type != "fav" && this.type != "lan" &&
  • {translate('Remove')}
  • } + {this.type != "fav" &&
  • {translate('Remove')}
  • } {is_win &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } @@ -429,6 +429,9 @@ class SessionList: Reactor.Component { break; } } + } else if (this.type == "lan") { + handler.remove_discovered(id); + app.update(); } else { handler.remove_peer(id); app.update(); From 7c323c86ac4f84a600d6f4f23030816fd4b98fae Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 18:14:50 +0800 Subject: [PATCH 04/10] lan_discovery_WOL: remove ununsed action for lan menu Signed-off-by: fufesou --- src/ui/ab.tis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 03768c6da..2fda2a5e1 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -319,9 +319,9 @@ class SessionList: Reactor.Component { {false && !handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }
  • RDP
  • -
  • {translate('Rename')}
  • + {this.type != "lan" &&
  • {translate('Rename')}
  • } {this.type != "fav" &&
  • {translate('Remove')}
  • } - {is_win &&
  • {translate('Create Desktop Shortcut')}
  • } + {is_win && this.type != "lan" &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } {(!this.type || this.type == "fav") &&
  • {translate('Remove from Favorites')}
  • } From 8fd4830710151aff5c7aa1795f554a75a4570743 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 14 Jul 2022 10:34:56 +0800 Subject: [PATCH 05/10] lan_discovery_WOL: Win10 test done Signed-off-by: fufesou --- Cargo.lock | 25 +++++++++++++++++++++ Cargo.toml | 1 + libs/hbb_common/src/config.rs | 2 +- src/rendezvous_mediator.rs | 42 ++++++++++++++++++++++++++++++----- src/ui.rs | 5 +++++ src/ui/ab.tis | 5 ++++- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 894382389..aa4c87dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -570,13 +570,28 @@ checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", "indexmap", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", ] +[[package]] +name = "clap_derive" +version = "3.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -4146,6 +4161,7 @@ dependencies = [ "winit", "winreg 0.10.1", "winres", + "wol-rs", ] [[package]] @@ -5713,6 +5729,15 @@ dependencies = [ "toml", ] +[[package]] +name = "wol-rs" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" +dependencies = [ + "clap 3.2.6", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 14cae5f9d..c008f7828 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ base64 = "0.13" sysinfo = "0.23" num_cpus = "1.13" default-net = "0.11.0" +wol-rs = "0.9.1" [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index ee8433770..8d2437c30 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -860,7 +860,7 @@ impl LocalConfig { pub struct DiscoveryPeer { pub id: String, #[serde(with = "serde_with::rust::map_as_tuple_list")] - pub mac_ips: HashMap, + pub ip_mac: HashMap, pub username: String, pub hostname: String, pub platform: String, diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index d87357366..f4f4fb15c 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -700,10 +700,9 @@ fn wait_response( if mac != p.mac { allow_err!(tx.send(config::DiscoveryPeer { id: p.id.clone(), - mac_ips: HashMap::from([( - p.mac.clone(), - addr.ip().to_string() - )]), + ip_mac: HashMap::from([ + (addr.ip().to_string(), p.mac.clone(),) + ]), username: p.username.clone(), hostname: p.hostname.clone(), platform: p.platform.clone(), @@ -744,7 +743,6 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) peer.online = false; }); - // handle received peers let mut response_set = HashSet::new(); let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); loop { @@ -753,20 +751,24 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) Some(peer) => { let in_response_set = !response_set.insert(peer.id.clone()); let mut pre_found = false; + // Try find and update peer for peer1 in &mut peers { if peer1.is_same_peer(&peer) { if in_response_set { - peer1.mac_ips.extend(peer.mac_ips.clone()); + // Merge ip_mac and update other infos + peer1.ip_mac.extend(peer.ip_mac.clone()); peer1.hostname = peer.hostname.clone(); peer1.platform = peer.platform.clone(); peer1.online = true; } else { + // Update all peer infos *peer1 = peer.clone(); } pre_found = true; break } } + // Push if not found if !pre_found { peers.push(peer); } @@ -796,3 +798,31 @@ pub async fn discover() -> ResultType<()> { log::info!("discover ping done"); Ok(()) } + +pub fn send_wol(id: String) { + let interfaces = default_net::get_interfaces(); + for peer in &config::LanPeers::load().peers { + if peer.id == id { + for (ip, mac) in peer.ip_mac.iter() { + if let Ok(mac_addr) = mac.parse() { + if let Ok(IpAddr::V4(ip)) = ip.parse() { + for interface in &interfaces { + for ipv4 in &interface.ipv4 { + if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) + == (u32::from(ip) & u32::from(ipv4.netmask)) + { + allow_err!(wol::send_wol( + mac_addr, + None, + Some(IpAddr::V4(ipv4.addr)) + )); + } + } + } + } + } + } + break; + } + } +} diff --git a/src/ui.rs b/src/ui.rs index 4bf201c0e..084422f6b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -547,6 +547,10 @@ impl UI { config::LanPeers::store(&peers); } + fn send_wol(&mut self, id: String) { + crate::rendezvous_mediator::send_wol(id) + } + fn new_remote(&mut self, id: String, remote_type: String) { let mut lock = self.0.lock().unwrap(); let args = vec![format!("--{}", remote_type), id.clone()]; @@ -786,6 +790,7 @@ impl sciter::EventHandler for UI { fn closing(i32, i32, i32, i32); fn get_size(); fn new_remote(String, bool); + fn send_wol(String); fn remove_peer(String); fn remove_discovered(String); fn get_connect_status(); diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 2fda2a5e1..5b822ed22 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -318,10 +318,11 @@ class SessionList: Reactor.Component {
  • {translate('TCP Tunneling')}
  • {false && !handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }
  • RDP
  • +
  • {translate('WOL')}
  • {this.type != "lan" &&
  • {translate('Rename')}
  • } {this.type != "fav" &&
  • {translate('Remove')}
  • } - {is_win && this.type != "lan" &&
  • {translate('Create Desktop Shortcut')}
  • } + {is_win &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } {(!this.type || this.type == "fav") &&
  • {translate('Remove from Favorites')}
  • } @@ -419,6 +420,8 @@ class SessionList: Reactor.Component { createNewConnect(id, "connect"); } else if (action == "transfer") { createNewConnect(id, "file-transfer"); + } else if (action == "wol") { + handler.send_wol(id); } else if (action == "remove") { if (this.type == "ab") { for (var i = 0; i < ab.peers.length; ++i) { From eda08555061a73e12b66246dd4428047794096ba Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 14 Jul 2022 03:35:00 +0800 Subject: [PATCH 06/10] lan_discovery_WOL: lan discovery test done Signed-off-by: fufesou --- src/rendezvous_mediator.rs | 8 +++++--- src/ui/ab.tis | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index f4f4fb15c..db087aa68 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -64,9 +64,11 @@ impl RendezvousMediator { direct_server(server_cloned).await; }); #[cfg(not(any(target_os = "android", target_os = "ios")))] - std::thread::spawn(move || { - allow_err!(lan_discovery()); - }); + if crate::platform::is_installed() { + std::thread::spawn(move || { + allow_err!(lan_discovery()); + }); + } loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 5b822ed22..28fa62352 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -532,7 +532,7 @@ class MultipleSessions: Reactor.Component {
    {translate('Recent Sessions')} {translate('Favorites')} - {{translate('Discovered')}} + {handler.is_installed() && {translate('Discovered')}} {translate('Address Book')}
    {!this.hidden && } @@ -540,7 +540,7 @@ class MultipleSessions: Reactor.Component {
    {!this.hidden && ((type == "fav" && ) || - (type == "lan" && ) || + (type == "lan" && handler.is_installed() && ) || (type == "ab" && ) || )}
    ; From 4955807dd081cb518da00be529973c2b80ab59c9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 11:35:23 +0800 Subject: [PATCH 07/10] lan_discovery_WOL: Update Cargo.lock Signed-off-by: fufesou --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa4c87dd8..adc152440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,9 +581,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.6" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -5735,7 +5735,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" dependencies = [ - "clap 3.2.6", + "clap 3.2.12", ] [[package]] From 13406cd359d3fd1c3902d4e5b9813d77c8fcf77b Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 12:16:15 +0800 Subject: [PATCH 08/10] lan_discovery_WOL: sort by online status Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 2 -- rust-toolchain.toml | 2 ++ src/rendezvous_mediator.rs | 29 +++++++---------------------- 3 files changed, 9 insertions(+), 24 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 8d2437c30..d05831c03 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -875,8 +875,6 @@ impl DiscoveryPeer { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LanPeers { - // #[serde(default)] - // pub peers: String, pub peers: Vec, } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..05dfa3270 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.62.0" diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index db087aa68..8da1937a7 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -750,31 +750,16 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) loop { tokio::select! { data = rx.recv() => match data { - Some(peer) => { + Some(mut peer) => { let in_response_set = !response_set.insert(peer.id.clone()); - let mut pre_found = false; - // Try find and update peer - for peer1 in &mut peers { - if peer1.is_same_peer(&peer) { - if in_response_set { - // Merge ip_mac and update other infos - peer1.ip_mac.extend(peer.ip_mac.clone()); - peer1.hostname = peer.hostname.clone(); - peer1.platform = peer.platform.clone(); - peer1.online = true; - } else { - // Update all peer infos - *peer1 = peer.clone(); - } - pre_found = true; - break + if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { + let peer1 = peers.remove(pos); + if in_response_set { + peer.ip_mac.extend(peer1.ip_mac); + peer.online = true; } } - // Push if not found - if !pre_found { - peers.push(peer); - } - + peers.insert(0, peer); if last_write_time.elapsed().as_millis() > 300 { config::LanPeers::store(&peers); last_write_time = Instant::now(); From 3613f27afc79c42ed4762a43670770f2079b438e Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 20:39:42 +0800 Subject: [PATCH 09/10] lan_discovery_WOL: fix udp set_nonblocking twice and lan refactor Signed-off-by: fufesou --- libs/hbb_common/src/udp.rs | 1 - src/lan.rs | 291 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/rendezvous_mediator.rs | 279 +---------------------------------- src/ui.rs | 4 +- 5 files changed, 298 insertions(+), 279 deletions(-) create mode 100644 src/lan.rs diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index c2d5057f2..3532dd1e0 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -37,7 +37,6 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result ResultType<()> { + let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); + let socket = std::net::UdpSocket::bind(addr)?; + socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; + log::info!("lan discovery listener started"); + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + if p.cmd == "ping" { + if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "pong".to_owned(), + mac: get_mac(&self_addr), + id: Config::get_id(), + hostname: whoami::hostname(), + username: crate::platform::get_active_username(), + platform: whoami::platform().to_string(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + } + } + } + _ => {} + } + } + } + } +} + +#[tokio::main(flavor = "current_thread")] +pub async fn discover() -> ResultType<()> { + let sockets = send_query()?; + let rx = spawn_wait_responses(sockets); + handle_received_peers(rx).await?; + + log::info!("discover ping done"); + Ok(()) +} + +pub fn send_wol(id: String) { + let interfaces = default_net::get_interfaces(); + for peer in &config::LanPeers::load().peers { + if peer.id == id { + for (ip, mac) in peer.ip_mac.iter() { + if let Ok(mac_addr) = mac.parse() { + if let Ok(IpAddr::V4(ip)) = ip.parse() { + for interface in &interfaces { + for ipv4 in &interface.ipv4 { + if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) + == (u32::from(ip) & u32::from(ipv4.netmask)) + { + allow_err!(wol::send_wol( + mac_addr, + None, + Some(IpAddr::V4(ipv4.addr)) + )); + } + } + } + } + } + } + break; + } + } +} + +#[inline] +fn get_broadcast_port() -> u16 { + (RENDEZVOUS_PORT + 3) as _ +} + +fn get_mac(ip: &IpAddr) -> String { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Ok(mac) = get_mac_by_ip(ip) { + mac.to_string() + } else { + "".to_owned() + } + #[cfg(any(target_os = "android", target_os = "ios"))] + "".to_owned() +} + +fn get_all_ipv4s() -> ResultType> { + let mut ipv4s = Vec::new(); + for interface in default_net::get_interfaces() { + for ipv4 in &interface.ipv4 { + ipv4s.push(ipv4.addr.clone()); + } + } + Ok(ipv4s) +} + +fn get_mac_by_ip(ip: &IpAddr) -> ResultType { + for interface in default_net::get_interfaces() { + match ip { + IpAddr::V4(local_ipv4) => { + if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + IpAddr::V6(local_ipv6) => { + if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + } + } + bail!("No interface found for ip: {:?}", ip); +} + +// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 +fn get_ipaddr_by_peer(peer: A) -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect(peer) { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip()), + Err(_) => return None, + }; +} + +fn create_broadcast_sockets() -> ResultType> { + let mut sockets = Vec::new(); + for v4_addr in get_all_ipv4s()? { + if v4_addr.is_private() { + let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; + s.set_broadcast(true)?; + log::debug!("Bind socket to {}", &v4_addr); + sockets.push(s) + } + } + Ok(sockets) +} + +fn send_query() -> ResultType> { + let sockets = create_broadcast_sockets()?; + if sockets.is_empty() { + bail!("Found no ipv4 addresses"); + } + + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "ping".to_owned(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); + for socket in &sockets { + socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + } + log::info!("discover ping sent"); + Ok(sockets) +} + +fn wait_response( + socket: UdpSocket, + timeout: Option, + tx: UnboundedSender, +) -> ResultType<()> { + let mut last_recv_time = Instant::now(); + + socket.set_read_timeout(timeout)?; + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + last_recv_time = Instant::now(); + if p.cmd == "pong" { + let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + get_mac(&self_addr) + } else { + "".to_owned() + }; + + if mac != p.mac { + allow_err!(tx.send(config::DiscoveryPeer { + id: p.id.clone(), + ip_mac: HashMap::from([ + (addr.ip().to_string(), p.mac.clone(),) + ]), + username: p.username.clone(), + hostname: p.hostname.clone(), + platform: p.platform.clone(), + online: true, + })); + } + } + } + _ => {} + } + } + } + if last_recv_time.elapsed().as_millis() > 3_000 { + break; + } + } + Ok(()) +} + +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { + let (tx, rx) = unbounded_channel::<_>(); + for socket in sockets { + let tx_clone = tx.clone(); + std::thread::spawn(move || { + allow_err!(wait_response( + socket, + Some(std::time::Duration::from_millis(10)), + tx_clone + )); + }); + } + rx +} + +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { + let mut peers = config::LanPeers::load().peers; + peers.iter_mut().for_each(|peer| { + peer.online = false; + }); + + let mut response_set = HashSet::new(); + let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + loop { + tokio::select! { + data = rx.recv() => match data { + Some(mut peer) => { + let in_response_set = !response_set.insert(peer.id.clone()); + if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { + let peer1 = peers.remove(pos); + if in_response_set { + peer.ip_mac.extend(peer1.ip_mac); + peer.online = true; + } + } + peers.insert(0, peer); + if last_write_time.elapsed().as_millis() > 300 { + config::LanPeers::store(&peers); + last_write_time = Instant::now(); + } + } + None => { + break + } + } + } + } + + config::LanPeers::store(&peers); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 93cd67738..715a59b5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ mod client; #[cfg(not(any(target_os = "ios")))] mod rendezvous_mediator; #[cfg(not(any(target_os = "ios")))] +mod lan; +#[cfg(not(any(target_os = "ios")))] pub use self::rendezvous_mediator::*; /// cbindgen:ignore pub mod common; diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 8da1937a7..6dc7a11c9 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -2,7 +2,7 @@ use crate::server::{check_zombie, new as new_server, ServerPtr}; use hbb_common::{ allow_err, anyhow::bail, - config::{self, Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, + config::{Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, futures::future::join_all, log, protobuf::Message as _, @@ -10,15 +10,13 @@ use hbb_common::{ sleep, socket_client, tokio::{ self, select, - sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, time::{interval, Duration}, }, udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; use std::{ - collections::{HashMap, HashSet}, - net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, + net::SocketAddr, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -66,7 +64,7 @@ impl RendezvousMediator { #[cfg(not(any(target_os = "android", target_os = "ios")))] if crate::platform::is_installed() { std::thread::spawn(move || { - allow_err!(lan_discovery()); + allow_err!(super::lan::start_listening()); }); } loop { @@ -542,274 +540,3 @@ async fn direct_server(server: ServerPtr) { } } } - -#[inline] -pub fn get_broadcast_port() -> u16 { - (RENDEZVOUS_PORT + 3) as _ -} - -pub fn get_mac(ip: &IpAddr) -> String { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(mac) = get_mac_by_ip(ip) { - mac.to_string() - } else { - "".to_owned() - } - #[cfg(any(target_os = "android", target_os = "ios"))] - "".to_owned() -} - -fn get_all_ipv4s() -> ResultType> { - let mut ipv4s = Vec::new(); - for interface in default_net::get_interfaces() { - for ipv4 in &interface.ipv4 { - ipv4s.push(ipv4.addr.clone()); - } - } - Ok(ipv4s) -} - -fn get_mac_by_ip(ip: &IpAddr) -> ResultType { - for interface in default_net::get_interfaces() { - match ip { - IpAddr::V4(local_ipv4) => { - if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { - if let Some(mac_addr) = interface.mac_addr { - return Ok(mac_addr.address()); - } - } - } - IpAddr::V6(local_ipv6) => { - if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { - if let Some(mac_addr) = interface.mac_addr { - return Ok(mac_addr.address()); - } - } - } - } - } - bail!("No interface found for ip: {:?}", ip); -} - -// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 -pub fn get_ipaddr_by_peer(peer: A) -> Option { - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(_) => return None, - }; - - match socket.connect(peer) { - Ok(()) => (), - Err(_) => return None, - }; - - match socket.local_addr() { - Ok(addr) => return Some(addr.ip()), - Err(_) => return None, - }; -} - -fn lan_discovery() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; - log::info!("lan discovery listener started"); - loop { - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::PeerDiscovery(p)) => { - if p.cmd == "ping" { - if let Some(self_addr) = get_ipaddr_by_peer(&addr) { - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "pong".to_owned(), - mac: get_mac(&self_addr), - id: Config::get_id(), - hostname: whoami::hostname(), - username: crate::platform::get_active_username(), - platform: whoami::platform().to_string(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); - } - } - } - _ => {} - } - } - } - } -} - -fn create_broadcast_sockets() -> ResultType> { - let mut sockets = Vec::new(); - for v4_addr in get_all_ipv4s()? { - if v4_addr.is_private() { - let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; - s.set_broadcast(true)?; - log::debug!("Bind socket to {}", &v4_addr); - sockets.push(s) - } - } - Ok(sockets) -} - -fn send_query() -> ResultType> { - let sockets = create_broadcast_sockets()?; - if sockets.is_empty() { - bail!("Found no ipv4 addresses"); - } - - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "ping".to_owned(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); - for socket in &sockets { - socket.send_to(&msg_out.write_to_bytes()?, maddr)?; - } - log::info!("discover ping sent"); - Ok(sockets) -} - -fn wait_response( - socket: UdpSocket, - timeout: Option, - tx: UnboundedSender, -) -> ResultType<()> { - let mut last_recv_time = Instant::now(); - - socket.set_read_timeout(timeout)?; - loop { - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::PeerDiscovery(p)) => { - last_recv_time = Instant::now(); - if p.cmd == "pong" { - let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { - get_mac(&self_addr) - } else { - "".to_owned() - }; - - if mac != p.mac { - allow_err!(tx.send(config::DiscoveryPeer { - id: p.id.clone(), - ip_mac: HashMap::from([ - (addr.ip().to_string(), p.mac.clone(),) - ]), - username: p.username.clone(), - hostname: p.hostname.clone(), - platform: p.platform.clone(), - online: true, - })); - } - } - } - _ => {} - } - } - } - if last_recv_time.elapsed().as_millis() > 3_000 { - break; - } - } - Ok(()) -} - -fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { - let (tx, rx) = unbounded_channel::<_>(); - for socket in sockets { - let tx_clone = tx.clone(); - std::thread::spawn(move || { - allow_err!(wait_response( - socket, - Some(std::time::Duration::from_millis(10)), - tx_clone - )); - }); - } - rx -} - -async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { - let mut peers = config::LanPeers::load().peers; - peers.iter_mut().for_each(|peer| { - peer.online = false; - }); - - let mut response_set = HashSet::new(); - let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); - loop { - tokio::select! { - data = rx.recv() => match data { - Some(mut peer) => { - let in_response_set = !response_set.insert(peer.id.clone()); - if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { - let peer1 = peers.remove(pos); - if in_response_set { - peer.ip_mac.extend(peer1.ip_mac); - peer.online = true; - } - } - peers.insert(0, peer); - if last_write_time.elapsed().as_millis() > 300 { - config::LanPeers::store(&peers); - last_write_time = Instant::now(); - } - } - None => { - break - } - } - } - } - - config::LanPeers::store(&peers); - Ok(()) -} - -#[tokio::main(flavor = "current_thread")] -pub async fn discover() -> ResultType<()> { - let sockets = send_query()?; - let rx = spawn_wait_responses(sockets); - handle_received_peers(rx).await?; - - log::info!("discover ping done"); - Ok(()) -} - -pub fn send_wol(id: String) { - let interfaces = default_net::get_interfaces(); - for peer in &config::LanPeers::load().peers { - if peer.id == id { - for (ip, mac) in peer.ip_mac.iter() { - if let Ok(mac_addr) = mac.parse() { - if let Ok(IpAddr::V4(ip)) = ip.parse() { - for interface in &interfaces { - for ipv4 in &interface.ipv4 { - if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) - == (u32::from(ip) & u32::from(ipv4.netmask)) - { - allow_err!(wol::send_wol( - mac_addr, - None, - Some(IpAddr::V4(ipv4.addr)) - )); - } - } - } - } - } - } - break; - } - } -} diff --git a/src/ui.rs b/src/ui.rs index 084422f6b..2b7ffcc29 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -548,7 +548,7 @@ impl UI { } fn send_wol(&mut self, id: String) { - crate::rendezvous_mediator::send_wol(id) + crate::lan::send_wol(id) } fn new_remote(&mut self, id: String, remote_type: String) { @@ -695,7 +695,7 @@ impl UI { fn discover(&self) { std::thread::spawn(move || { - allow_err!(crate::rendezvous_mediator::discover()); + allow_err!(crate::lan::discover()); }); } From 52f4f274b24ca9f0302482ff2201b1a230542ffb Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 17 Jul 2022 22:46:55 +0800 Subject: [PATCH 10/10] lan_discovery_WOL: remove unused struct MyConfig Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d05831c03..340a725f8 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -878,12 +878,6 @@ pub struct LanPeers { pub peers: Vec, } -#[derive(Serialize, Deserialize)] -struct MyConfig { - version: u8, - api_key: String, -} - impl LanPeers { pub fn load() -> LanPeers { let _ = CONFIG.read().unwrap(); // for lock