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" && ) || )} ;