lan.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. use hbb_common::config::Config;
  2. use hbb_common::{
  3. allow_err,
  4. anyhow::bail,
  5. config::{self, RENDEZVOUS_PORT},
  6. log,
  7. protobuf::Message as _,
  8. rendezvous_proto::*,
  9. tokio::{
  10. self,
  11. sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
  12. },
  13. ResultType,
  14. };
  15. use std::{
  16. collections::{HashMap, HashSet},
  17. net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket},
  18. time::Instant,
  19. };
  20. type Message = RendezvousMessage;
  21. #[cfg(not(target_os = "ios"))]
  22. pub(super) fn start_listening() -> ResultType<()> {
  23. let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port()));
  24. let socket = std::net::UdpSocket::bind(addr)?;
  25. socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?;
  26. log::info!("lan discovery listener started");
  27. loop {
  28. let mut buf = [0; 2048];
  29. if let Ok((len, addr)) = socket.recv_from(&mut buf) {
  30. if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) {
  31. match msg_in.union {
  32. Some(rendezvous_message::Union::PeerDiscovery(p)) => {
  33. if p.cmd == "ping"
  34. && config::option2bool(
  35. "enable-lan-discovery",
  36. &Config::get_option("enable-lan-discovery"),
  37. )
  38. {
  39. let id = Config::get_id();
  40. if p.id == id {
  41. continue;
  42. }
  43. if let Some(self_addr) = get_ipaddr_by_peer(&addr) {
  44. let mut msg_out = Message::new();
  45. let mut hostname = whoami::hostname();
  46. // The default hostname is "localhost" which is a bit confusing
  47. if hostname == "localhost" {
  48. hostname = "unknown".to_owned();
  49. }
  50. let peer = PeerDiscovery {
  51. cmd: "pong".to_owned(),
  52. mac: get_mac(&self_addr),
  53. id,
  54. hostname,
  55. username: crate::platform::get_active_username(),
  56. platform: whoami::platform().to_string(),
  57. ..Default::default()
  58. };
  59. msg_out.set_peer_discovery(peer);
  60. socket.send_to(&msg_out.write_to_bytes()?, addr).ok();
  61. }
  62. }
  63. }
  64. _ => {}
  65. }
  66. }
  67. }
  68. }
  69. }
  70. #[tokio::main(flavor = "current_thread")]
  71. pub async fn discover() -> ResultType<()> {
  72. let sockets = send_query()?;
  73. let rx = spawn_wait_responses(sockets);
  74. handle_received_peers(rx).await?;
  75. log::info!("discover ping done");
  76. Ok(())
  77. }
  78. pub fn send_wol(id: String) {
  79. let interfaces = default_net::get_interfaces();
  80. for peer in &config::LanPeers::load().peers {
  81. if peer.id == id {
  82. for (_, mac) in peer.ip_mac.iter() {
  83. if let Ok(mac_addr) = mac.parse() {
  84. for interface in &interfaces {
  85. for ipv4 in &interface.ipv4 {
  86. // remove below mask check to avoid unexpected bug
  87. // if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) == (u32::from(peer_ip) & u32::from(ipv4.netmask))
  88. log::info!("Send wol to {mac_addr} of {}", ipv4.addr);
  89. allow_err!(wol::send_wol(mac_addr, None, Some(IpAddr::V4(ipv4.addr))));
  90. }
  91. }
  92. }
  93. }
  94. break;
  95. }
  96. }
  97. }
  98. #[inline]
  99. fn get_broadcast_port() -> u16 {
  100. (RENDEZVOUS_PORT + 3) as _
  101. }
  102. fn get_mac(_ip: &IpAddr) -> String {
  103. #[cfg(not(target_os = "ios"))]
  104. if let Ok(mac) = get_mac_by_ip(_ip) {
  105. mac.to_string()
  106. } else {
  107. "".to_owned()
  108. }
  109. #[cfg(target_os = "ios")]
  110. "".to_owned()
  111. }
  112. #[cfg(not(target_os = "ios"))]
  113. fn get_mac_by_ip(ip: &IpAddr) -> ResultType<String> {
  114. for interface in default_net::get_interfaces() {
  115. match ip {
  116. IpAddr::V4(local_ipv4) => {
  117. if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) {
  118. if let Some(mac_addr) = interface.mac_addr {
  119. return Ok(mac_addr.address());
  120. }
  121. }
  122. }
  123. IpAddr::V6(local_ipv6) => {
  124. if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) {
  125. if let Some(mac_addr) = interface.mac_addr {
  126. return Ok(mac_addr.address());
  127. }
  128. }
  129. }
  130. }
  131. }
  132. bail!("No interface found for ip: {:?}", ip);
  133. }
  134. // Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4
  135. fn get_ipaddr_by_peer<A: ToSocketAddrs>(peer: A) -> Option<IpAddr> {
  136. let socket = match UdpSocket::bind("0.0.0.0:0") {
  137. Ok(s) => s,
  138. Err(_) => return None,
  139. };
  140. match socket.connect(peer) {
  141. Ok(()) => (),
  142. Err(_) => return None,
  143. };
  144. match socket.local_addr() {
  145. Ok(addr) => return Some(addr.ip()),
  146. Err(_) => return None,
  147. };
  148. }
  149. fn create_broadcast_sockets() -> Vec<UdpSocket> {
  150. let mut ipv4s = Vec::new();
  151. // TODO: maybe we should use a better way to get ipv4 addresses.
  152. // But currently, it's ok to use `[Ipv4Addr::UNSPECIFIED]` for discovery.
  153. // `default_net::get_interfaces()` causes undefined symbols error when `flutter build` on iOS simulator x86_64
  154. #[cfg(not(any(target_os = "ios")))]
  155. for interface in default_net::get_interfaces() {
  156. for ipv4 in &interface.ipv4 {
  157. ipv4s.push(ipv4.addr.clone());
  158. }
  159. }
  160. ipv4s.push(Ipv4Addr::UNSPECIFIED); // for robustness
  161. let mut sockets = Vec::new();
  162. for v4_addr in ipv4s {
  163. // removing v4_addr.is_private() check, https://github.com/rustdesk/rustdesk/issues/4663
  164. if let Ok(s) = UdpSocket::bind(SocketAddr::from((v4_addr, 0))) {
  165. if s.set_broadcast(true).is_ok() {
  166. sockets.push(s);
  167. }
  168. }
  169. }
  170. sockets
  171. }
  172. fn send_query() -> ResultType<Vec<UdpSocket>> {
  173. let sockets = create_broadcast_sockets();
  174. if sockets.is_empty() {
  175. bail!("Found no bindable ipv4 addresses");
  176. }
  177. let mut msg_out = Message::new();
  178. // We may not be able to get the mac address on mobile platforms.
  179. // So we need to use the id to avoid discovering ourselves.
  180. #[cfg(any(target_os = "android", target_os = "ios"))]
  181. let id = crate::ui_interface::get_id();
  182. // `crate::ui_interface::get_id()` will cause error:
  183. // `get_id()` uses async code with `current_thread`, which is not allowed in this context.
  184. //
  185. // No need to get id for desktop platforms.
  186. // We can use the mac address to identify the device.
  187. #[cfg(not(any(target_os = "android", target_os = "ios")))]
  188. let id = "".to_owned();
  189. let peer = PeerDiscovery {
  190. cmd: "ping".to_owned(),
  191. id,
  192. ..Default::default()
  193. };
  194. msg_out.set_peer_discovery(peer);
  195. let out = msg_out.write_to_bytes()?;
  196. let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port()));
  197. for socket in &sockets {
  198. allow_err!(socket.send_to(&out, maddr));
  199. }
  200. log::info!("discover ping sent");
  201. Ok(sockets)
  202. }
  203. fn wait_response(
  204. socket: UdpSocket,
  205. timeout: Option<std::time::Duration>,
  206. tx: UnboundedSender<config::DiscoveryPeer>,
  207. ) -> ResultType<()> {
  208. let mut last_recv_time = Instant::now();
  209. let local_addr = socket.local_addr();
  210. let try_get_ip_by_peer = match local_addr.as_ref() {
  211. Err(..) => true,
  212. Ok(addr) => addr.ip().is_unspecified(),
  213. };
  214. let mut mac: Option<String> = None;
  215. socket.set_read_timeout(timeout)?;
  216. loop {
  217. let mut buf = [0; 2048];
  218. if let Ok((len, addr)) = socket.recv_from(&mut buf) {
  219. if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) {
  220. match msg_in.union {
  221. Some(rendezvous_message::Union::PeerDiscovery(p)) => {
  222. last_recv_time = Instant::now();
  223. if p.cmd == "pong" {
  224. let local_mac = if try_get_ip_by_peer {
  225. if let Some(self_addr) = get_ipaddr_by_peer(&addr) {
  226. get_mac(&self_addr)
  227. } else {
  228. "".to_owned()
  229. }
  230. } else {
  231. match mac.as_ref() {
  232. Some(m) => m.clone(),
  233. None => {
  234. let m = if let Ok(local_addr) = local_addr {
  235. get_mac(&local_addr.ip())
  236. } else {
  237. "".to_owned()
  238. };
  239. mac = Some(m.clone());
  240. m
  241. }
  242. }
  243. };
  244. if local_mac.is_empty() && p.mac.is_empty() || local_mac != p.mac {
  245. allow_err!(tx.send(config::DiscoveryPeer {
  246. id: p.id.clone(),
  247. ip_mac: HashMap::from([
  248. (addr.ip().to_string(), p.mac.clone(),)
  249. ]),
  250. username: p.username.clone(),
  251. hostname: p.hostname.clone(),
  252. platform: p.platform.clone(),
  253. online: true,
  254. }));
  255. }
  256. }
  257. }
  258. _ => {}
  259. }
  260. }
  261. }
  262. if last_recv_time.elapsed().as_millis() > 3_000 {
  263. break;
  264. }
  265. }
  266. Ok(())
  267. }
  268. fn spawn_wait_responses(sockets: Vec<UdpSocket>) -> UnboundedReceiver<config::DiscoveryPeer> {
  269. let (tx, rx) = unbounded_channel::<_>();
  270. for socket in sockets {
  271. let tx_clone = tx.clone();
  272. std::thread::spawn(move || {
  273. allow_err!(wait_response(
  274. socket,
  275. Some(std::time::Duration::from_millis(10)),
  276. tx_clone
  277. ));
  278. });
  279. }
  280. rx
  281. }
  282. async fn handle_received_peers(mut rx: UnboundedReceiver<config::DiscoveryPeer>) -> ResultType<()> {
  283. let mut peers = config::LanPeers::load().peers;
  284. peers.iter_mut().for_each(|peer| {
  285. peer.online = false;
  286. });
  287. let mut response_set = HashSet::new();
  288. let mut last_write_time: Option<Instant> = None;
  289. loop {
  290. tokio::select! {
  291. data = rx.recv() => match data {
  292. Some(mut peer) => {
  293. let in_response_set = !response_set.insert(peer.id.clone());
  294. if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) {
  295. let peer1 = peers.remove(pos);
  296. if in_response_set {
  297. peer.ip_mac.extend(peer1.ip_mac);
  298. peer.online = true;
  299. }
  300. }
  301. peers.insert(0, peer);
  302. if last_write_time.map(|t| t.elapsed().as_millis() > 300).unwrap_or(true) {
  303. config::LanPeers::store(&peers);
  304. #[cfg(feature = "flutter")]
  305. crate::flutter_ffi::main_load_lan_peers();
  306. last_write_time = Some(Instant::now());
  307. }
  308. }
  309. None => {
  310. break
  311. }
  312. }
  313. }
  314. }
  315. config::LanPeers::store(&peers);
  316. #[cfg(feature = "flutter")]
  317. crate::flutter_ffi::main_load_lan_peers();
  318. Ok(())
  319. }