1use std::net::{IpAddr, Ipv4Addr, SocketAddr};
4use std::sync::Arc;
5use std::sync::atomic::{AtomicU16, Ordering};
6use std::time::Duration;
7
8use spvirit_codec::spvirit_encode::{encode_beacon, ip_to_bytes};
9use tokio::net::UdpSocket;
10
11pub struct BeaconConfig {
13 pub target: SocketAddr,
14 pub guid: [u8; 12],
15 pub tcp_port: u16,
16 pub advertise_ip: Option<IpAddr>,
17 pub listen_ip: IpAddr,
18 pub period_secs: u64,
19}
20
21pub async fn run_beacon(
23 config: BeaconConfig,
24 change_counter: Arc<AtomicU16>,
25) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
26 if config.period_secs == 0 {
27 return Ok(());
28 }
29 let bind_addr = if config.target.is_ipv4() {
30 "0.0.0.0:0"
31 } else {
32 "[::]:0"
33 };
34 let socket = UdpSocket::bind(bind_addr).await?;
35 socket.set_broadcast(true)?;
36 let mut interval = tokio::time::interval(Duration::from_secs(config.period_secs));
37 let mut seq: u8 = 0;
38
39 loop {
40 interval.tick().await;
41 let resp_ip = if let Some(ip) = config.advertise_ip {
42 ip
43 } else if !config.listen_ip.is_unspecified() {
44 config.listen_ip
45 } else {
46 IpAddr::V4(Ipv4Addr::UNSPECIFIED)
47 };
48 let addr_bytes = if resp_ip.is_unspecified() {
49 [0u8; 16]
50 } else {
51 ip_to_bytes(resp_ip)
52 };
53 let change_count = change_counter.load(Ordering::SeqCst);
54 let msg = encode_beacon(
55 config.guid,
56 seq,
57 change_count,
58 addr_bytes,
59 config.tcp_port,
60 "tcp",
61 2,
62 false,
63 );
64 let _ = socket.send_to(&msg, config.target).await;
65 seq = seq.wrapping_add(1);
66 }
67}