use std::{io, net::SocketAddr, time::Duration};
use sha2::{Digest, Sha256};
#[must_use]
pub fn derive_channel_id(phrase: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(phrase.as_bytes());
let result = hasher.finalize();
hex::encode(&result[..4])
}
pub async fn start_broadcaster(channel_id: String, port: u16) -> Result<(), io::Error> {
let socket = compio::net::UdpSocket::bind("0.0.0.0:0").await?;
socket.set_broadcast(true)?;
let msg = format!(
"HAYATE_PEER:{}:{}:{}",
channel_id,
std::env::consts::OS,
port
);
let target: SocketAddr = "255.255.255.255:50002".parse().unwrap();
let loopback: SocketAddr = "127.0.0.1:50002".parse().unwrap();
loop {
let compio::BufResult(res, _) = socket.send_to(msg.as_bytes().to_vec(), target).await;
if let Err(_e) = res {
}
let compio::BufResult(res, _) = socket.send_to(msg.as_bytes().to_vec(), loopback).await;
if let Err(_e) = res {
}
compio::time::sleep(Duration::from_secs(1)).await;
}
}
pub async fn listen_for_broadcast(
target_phrase: Option<String>,
timeout: Duration,
) -> Result<Option<(String, SocketAddr, String)>, io::Error> {
let target_channel_id = target_phrase.as_deref().map(derive_channel_id);
let std_socket = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::DGRAM,
Some(socket2::Protocol::UDP),
)?;
std_socket.set_reuse_address(true)?;
#[cfg(not(windows))]
std_socket.set_reuse_port(true)?;
let listen_addr: SocketAddr = "0.0.0.0:50002".parse().unwrap();
std_socket.bind(&socket2::SockAddr::from(listen_addr))?;
let socket = compio::net::UdpSocket::from_std(std_socket.into())?;
let buf = vec![0u8; 1024];
let res = compio::time::timeout(timeout, async move {
let mut temp_buf = buf;
loop {
let compio::BufResult(recv_res, b) = socket.recv_from(temp_buf).await;
temp_buf = b;
match recv_res {
Ok((n, src_addr)) => {
let data = &temp_buf[..n];
if let Ok(text) = std::str::from_utf8(data) {
let parts: Vec<&str> = text.split(':').collect();
if parts.len() == 4 && parts[0] == "HAYATE_PEER" {
let matches = match &target_channel_id {
Some(expected_id) => parts[1] == expected_id,
None => true,
};
if matches && let Ok(port) = parts[3].parse::<u16>() {
let peer_addr = SocketAddr::new(src_addr.ip(), port);
let os = parts[2].to_owned();
return Ok(Some(("Hayate Peer".to_owned(), peer_addr, os)));
}
}
}
}
Err(e) => return Err(e),
}
}
})
.await;
match res {
Ok(inner_res) => inner_res,
Err(_) => Ok(None), }
}