lan_discovery_WOL: mid commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2022-07-12 22:34:05 +08:00
parent d3fc6ccd9c
commit 7e0f7be95c
4 changed files with 256 additions and 41 deletions

83
Cargo.lock generated
View File

@ -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"

View File

@ -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 }

View File

@ -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<Vec<Ipv4Addr>> {
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<String> {
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<A: ToSocketAddrs>(peer: A) -> Option<IpAddr> {
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<Vec<UdpSocket>> {
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<Vec<UdpSocket>> {
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<std::time::Duration>,
) -> ResultType<Vec<DiscoveryPeer>> {
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<DiscoveryPeer> = 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(())

View File

@ -526,7 +526,7 @@ class MultipleSessions: Reactor.Component {
<div style="width:*" .sessions-tab #sessions-type>
<span class={!type ? 'active' : 'inactive'}>{translate('Recent Sessions')}</span>
<span #fav class={type == "fav" ? 'active' : 'inactive'}>{translate('Favorites')}</span>
{handler.is_installed() && <span #lan class={type == "lan" ? 'active' : 'inactive'}>{translate('Discovered')}</span>}
{<span #lan class={type == "lan" ? 'active' : 'inactive'}>{translate('Discovered')}</span>}
<span #ab class={type == "ab" ? 'active' : 'inactive'}>{translate('Address Book')}</span>
</div>
{!this.hidden && <SearchBar type={type} />}
@ -534,7 +534,7 @@ class MultipleSessions: Reactor.Component {
</div>
{!this.hidden &&
((type == "fav" && <Favorites />) ||
(type == "lan" && handler.is_installed() && <LanPeers />) ||
(type == "lan" && <LanPeers />) ||
(type == "ab" && <AddressBook />) ||
<SessionList sessions={handler.get_recent_sessions()} />)}
</div>;