alpine_protocol_sdk/
discovery.rs1use std::{
2 fmt, io,
3 net::{IpAddr, SocketAddr, UdpSocket},
4 time::Duration,
5};
6
7use alpine::messages::{DiscoveryReply, DiscoveryRequest};
8use rand::{rngs::OsRng, RngCore};
9use serde_cbor;
10use tracing::trace;
11
12const DEFAULT_MULTICAST_IPV4: &str = "239.255.255.250:5555";
13const DEFAULT_MULTICAST_IPV6: &str = "[ff12::1]:5555";
14const DEFAULT_BROADCAST_IPV4: &str = "255.255.255.255:5555";
15
16pub struct DiscoveryClientOptions {
18 pub remote_addr: SocketAddr,
19 pub local_addr: SocketAddr,
20 pub timeout: Duration,
21 pub prefer_multicast: bool,
22}
23
24impl DiscoveryClientOptions {
25 pub fn new(remote_addr: SocketAddr, local_addr: SocketAddr, timeout: Duration) -> Self {
27 Self {
28 remote_addr,
29 local_addr,
30 timeout,
31 prefer_multicast: true,
32 }
33 }
34
35 pub fn disable_multicast(mut self) -> Self {
36 self.prefer_multicast = false;
37 self
38 }
39}
40
41#[derive(Debug)]
43pub enum DiscoveryError {
44 Io(io::Error),
45 Decode(serde_cbor::Error),
46 Timeout,
47 PermissionDenied,
48 MulticastUnavailable,
49 BroadcastBlocked,
50}
51
52impl fmt::Display for DiscoveryError {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 DiscoveryError::Io(err) => write!(f, "io error: {}", err),
56 DiscoveryError::Decode(err) => write!(f, "cbors serialization error: {}", err),
57 DiscoveryError::Timeout => write!(f, "discovery timed out"),
58 DiscoveryError::PermissionDenied => {
59 write!(f, "discovery channel permission denied")
60 }
61 DiscoveryError::MulticastUnavailable => {
62 write!(f, "multicast discovery unavailable")
63 }
64 DiscoveryError::BroadcastBlocked => write!(f, "broadcast discovery blocked"),
65 }
66 }
67}
68
69impl std::error::Error for DiscoveryError {}
70
71impl From<io::Error> for DiscoveryError {
72 fn from(err: io::Error) -> Self {
73 match err.kind() {
74 io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock => DiscoveryError::Timeout,
75 _ => DiscoveryError::Io(err),
76 }
77 }
78}
79
80impl From<serde_cbor::Error> for DiscoveryError {
81 fn from(err: serde_cbor::Error) -> Self {
82 DiscoveryError::Decode(err)
83 }
84}
85
86pub struct DiscoveryOutcome {
88 pub reply: DiscoveryReply,
89 pub peer: SocketAddr,
90}
91
92pub struct DiscoveryClient {
94 socket: UdpSocket,
95 remote_addr: SocketAddr,
96 prefer_multicast: bool,
97}
98
99impl DiscoveryClient {
100 pub fn new(options: DiscoveryClientOptions) -> Result<Self, DiscoveryError> {
102 let socket = UdpSocket::bind(options.local_addr)?;
103 socket.set_broadcast(true)?;
104 socket.set_read_timeout(Some(options.timeout))?;
105 Ok(Self {
106 socket,
107 remote_addr: options.remote_addr,
108 prefer_multicast: options.prefer_multicast,
109 })
110 }
111
112 pub fn discover(&self, requested: &[String]) -> Result<DiscoveryOutcome, DiscoveryError> {
114 let mut nonce = vec![0u8; 32];
115 OsRng.fill_bytes(&mut nonce);
116 let request = DiscoveryRequest::new(requested.to_vec(), nonce.clone());
117 let payload = serde_cbor::to_vec(&request)?;
118
119 let mut send_error: Option<DiscoveryError> = None;
120 let mut sent = false;
121 for target in self.discovery_targets() {
122 match self.socket.send_to(&payload, target) {
123 Ok(_) => {
124 trace!(target = %target, "discovery payload sent");
125 sent = true;
126 break;
127 }
128 Err(err) => {
129 let kind = err.kind();
130 let mapped = self.map_send_error(err, target);
131 trace!(target = %target, error = %mapped, "discovery send failed");
132 send_error = Some(mapped);
133 if !self.should_continue_after_error(&kind) {
134 return Err(send_error.unwrap());
135 }
136 }
137 }
138 }
139
140 if !sent {
141 return Err(send_error.unwrap_or_else(|| DiscoveryError::PermissionDenied));
142 }
143
144 let mut buf = vec![0u8; 2048];
145 let (len, peer) = match self.socket.recv_from(&mut buf) {
146 Ok(res) => res,
147 Err(err) => return Err(self.map_recv_error(err)),
148 };
149 let reply: DiscoveryReply = serde_cbor::from_slice(&buf[..len])?;
150 Ok(DiscoveryOutcome { reply, peer })
151 }
152
153 fn discovery_targets(&self) -> Vec<SocketAddr> {
154 let mut targets = Vec::new();
155
156 if self.prefer_multicast {
157 if let Ok(addr) = DEFAULT_MULTICAST_IPV4.parse() {
158 targets.push(addr);
159 }
160 if let Ok(addr) = DEFAULT_MULTICAST_IPV6.parse() {
161 targets.push(addr);
162 }
163 }
164
165 if !targets.contains(&self.remote_addr) {
166 targets.push(self.remote_addr);
167 }
168
169 if self.remote_addr.ip().is_ipv4() {
170 if let Ok(addr) = DEFAULT_BROADCAST_IPV4.parse() {
171 if !targets.contains(&addr) {
172 targets.push(addr);
173 }
174 }
175 }
176
177 targets
178 }
179
180 fn map_send_error(&self, err: io::Error, target: SocketAddr) -> DiscoveryError {
181 match err.kind() {
182 io::ErrorKind::PermissionDenied => {
183 if target.ip().is_multicast() {
184 DiscoveryError::MulticastUnavailable
185 } else if is_broadcast_addr(target.ip()) {
186 DiscoveryError::BroadcastBlocked
187 } else {
188 DiscoveryError::PermissionDenied
189 }
190 }
191 io::ErrorKind::ConnectionReset | io::ErrorKind::WouldBlock => {
192 DiscoveryError::PermissionDenied
193 }
194 _ => DiscoveryError::Io(err),
195 }
196 }
197
198 fn map_recv_error(&self, err: io::Error) -> DiscoveryError {
199 match err.kind() {
200 io::ErrorKind::TimedOut => DiscoveryError::Timeout,
201 io::ErrorKind::PermissionDenied
202 | io::ErrorKind::ConnectionReset
203 | io::ErrorKind::WouldBlock => DiscoveryError::PermissionDenied,
204 _ => DiscoveryError::Io(err),
205 }
206 }
207
208 fn should_continue_after_error(&self, kind: &io::ErrorKind) -> bool {
209 matches!(
210 kind,
211 io::ErrorKind::PermissionDenied
212 | io::ErrorKind::WouldBlock
213 | io::ErrorKind::ConnectionReset
214 )
215 }
216}
217
218fn is_broadcast_addr(ip: IpAddr) -> bool {
219 matches!(ip, IpAddr::V4(addr) if addr.is_broadcast())
220}