liminal_server/link_punch/
recovery.rs1use super::{PunchError, PunchedLink, Puncher, UdpCarrier, fresh_nonce, require};
3use crate::link_handshake::MachineIdentity;
4use serde_json::json;
5use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
6use std::sync::Arc;
7
8impl Puncher {
9 pub fn rebind_broker(&mut self, mut broker: super::WireBroker) -> Result<(), PunchError> {
13 broker.subscribe(self.local.machine())?;
14 self.broker = broker;
15 Ok(())
16 }
17 pub fn prepare_open(&mut self) -> Result<(), PunchError> {
21 if self.observed.is_some() {
22 return Ok(());
23 }
24 let nonce = fresh_nonce()?;
25 let mut request = [0; 20];
26 request[..4].copy_from_slice(b"LMOB");
27 request[4..].copy_from_slice(&nonce);
28 let (answer, source) = self
29 .carrier()?
30 .observe_raw(&request, self.echo, self.bound)?;
31 self.observed = Some(observed_endpoint(&nonce, self.echo, source, &answer)?);
32 self.register()
33 }
34 pub fn shared_carrier(&self) -> Result<Arc<UdpCarrier>, PunchError> {
38 self.carrier
39 .clone()
40 .ok_or(PunchError::Named("device_not_observed"))
41 }
42
43 pub fn lookup_addresses(&mut self, peer: MachineIdentity) -> Result<Vec<String>, PunchError> {
47 self.validate_peer(peer)?;
48 let value = self.broker.call(
49 "net.broker.lookup",
50 &json!({"device": hex::encode(peer.ed25519_public())}),
51 )?;
52 serde_json::from_value(
53 value
54 .get("addresses")
55 .cloned()
56 .ok_or(PunchError::Named("invalid_broker_answer"))?,
57 )
58 .map_err(|_| PunchError::Named("invalid_broker_answer"))
59 }
60
61 pub fn refresh(&mut self, peer: MachineIdentity, index: u32) -> Result<(), PunchError> {
65 self.validate_peer(peer)?;
66 let lookup = self.broker.call(
67 "net.broker.lookup",
68 &json!({"device": hex::encode(peer.ed25519_public())}),
69 )?;
70 let endpoint: SocketAddr = serde_json::from_value(
71 lookup
72 .get("observed")
73 .cloned()
74 .ok_or(PunchError::Named("peer_not_observed"))?,
75 )
76 .map_err(|_| PunchError::Named("peer_not_observed"))?;
77 let nonce = punch_nonce(false)?;
78 self.probe(endpoint)?;
79 self.request_punch(peer, &nonce)?;
80 let word = self.broker.word(peer, Some(&nonce))?;
81 require(word.observed == Some(endpoint), "peer_observation_changed")?;
82 self.carrier()?.refresh_endpoint(index, endpoint)?;
83 Ok(())
84 }
85
86 pub fn accept_refresh(&mut self, peer: MachineIdentity, index: u32) -> Result<(), PunchError> {
90 self.validate_peer(peer)?;
91 let word = self.broker.word(peer, None)?;
92 let endpoint = word
93 .observed
94 .ok_or(PunchError::Named("peer_not_observed"))?;
95 self.probe(endpoint)?;
96 self.request_punch(peer, &word.nonce)?;
97 self.carrier()?.refresh_endpoint(index, endpoint)?;
98 Ok(())
99 }
100 pub fn punch(&mut self, peer: MachineIdentity, index: u32) -> Result<PunchedLink, PunchError> {
104 self.validate_peer(peer)?;
105 require(self.observed.take().is_some(), "device_not_observed")?;
106 let nonce = punch_nonce(true)?;
107 let lookup = self.broker.call(
108 "net.broker.lookup",
109 &json!({"device": hex::encode(peer.ed25519_public())}),
110 )?;
111 let endpoint: SocketAddr = serde_json::from_value(
112 lookup
113 .get("observed")
114 .cloned()
115 .ok_or(PunchError::Named("peer_not_observed"))?,
116 )
117 .map_err(|_| PunchError::Named("peer_not_observed"))?;
118 self.probe(endpoint)?;
119 self.request_punch(peer, &nonce)?;
120 let word = self.broker.word(peer, Some(&nonce))?;
121 require(word.observed == Some(endpoint), "peer_observation_changed")?;
122 self.handshake(peer, endpoint, index)
123 }
124
125 pub fn accept(&mut self, peer: MachineIdentity, index: u32) -> Result<PunchedLink, PunchError> {
129 self.validate_peer(peer)?;
130 require(self.observed.take().is_some(), "device_not_observed")?;
131 let word = self.broker.word(peer, None)?;
132 let endpoint = word
133 .observed
134 .ok_or(PunchError::Named("peer_not_observed"))?;
135 self.probe(endpoint)?;
136 self.request_punch(peer, &word.nonce)?;
138 self.handshake(peer, endpoint, index)
139 }
140
141 pub fn carrier(&self) -> Result<&UdpCarrier, PunchError> {
145 self.carrier
146 .as_deref()
147 .ok_or(PunchError::Named("device_not_observed"))
148 }
149}
150
151pub(super) fn observed_endpoint(
152 nonce: &[u8; 16],
153 expected: SocketAddr,
154 source: SocketAddr,
155 answer: &[u8],
156) -> Result<SocketAddr, PunchError> {
157 let length = answer.len();
158 if source != expected || length < 23 || &answer[..4] != b"LMOA" || answer[4..20] != *nonce {
159 return Err(PunchError::Named("invalid_broker_echo"));
160 }
161 let address = match (answer[20], length) {
162 (4, 27) => SocketAddr::from((
163 Ipv4Addr::new(answer[21], answer[22], answer[23], answer[24]),
164 u16::from_be_bytes([answer[25], answer[26]]),
165 )),
166 (6, 39) => SocketAddr::from((
167 Ipv6Addr::from(
168 <[u8; 16]>::try_from(&answer[21..37])
169 .map_err(|_| PunchError::Named("invalid_broker_echo"))?,
170 ),
171 u16::from_be_bytes([answer[37], answer[38]]),
172 )),
173 _ => return Err(PunchError::Named("invalid_broker_echo")),
174 };
175 require(address.port() != 0, "invalid_broker_echo")?;
176 Ok(address)
177}
178
179#[derive(Debug)]
181pub struct Instruction {
182 pub peer: MachineIdentity,
183 pub endpoint: SocketAddr,
184 pub nonce: String,
185}
186impl Puncher {
187 pub fn fork_listener(
191 &self,
192 local: super::LocalMachine,
193 mut broker: super::WireBroker,
194 ) -> Result<Self, PunchError> {
195 broker.subscribe(local.machine())?;
196 broker.words.set_read_timeout(None)?;
197 Ok(Self {
198 local,
199 socket: Arc::clone(&self.socket),
200 broker,
201 echo: self.echo,
202 bound: self.bound,
203 observed: self.observed,
204 carrier: Some(self.shared_carrier()?),
205 attempted: true,
206 requests: Arc::clone(&self.requests),
207 })
208 }
209 pub fn next_instruction(&mut self) -> Result<Instruction, PunchError> {
213 loop {
214 let Some(liminal::protocol::Frame::Deliver {
215 stream_id: 1,
216 envelope,
217 ..
218 }) = crate::link_carrier::read_frame(&mut self.broker.words)?
219 else {
220 return Err(PunchError::Named("punch_word_missing"));
221 };
222 require(
223 Some(envelope.schema_id) == self.broker.schema,
224 "invalid_punch_schema",
225 )?;
226 let word: super::Word = serde_json::from_slice(&envelope.payload)
227 .map_err(|_| PunchError::Named("invalid_punch_word"))?;
228 let mut key = [0; 32];
229 hex::decode_to_slice(&word.from, &mut key)
230 .map_err(|_| PunchError::Named("invalid_punch_peer"))?;
231 require(
232 hex::decode(&word.nonce).is_ok_and(|nonce| nonce.len() == 16),
233 "invalid_punch_nonce",
234 )?;
235 if self
236 .requests
237 .lock()
238 .map_err(|_| PunchError::Named("punch_request_state_poisoned"))?
239 .contains(&word.nonce)
240 {
241 continue;
242 }
243 let peer = MachineIdentity::new(key);
244 self.validate_peer(peer)?;
245 let endpoint = word
246 .observed
247 .ok_or(PunchError::Named("peer_not_observed"))?;
248 require(endpoint.port() != 0, "peer_not_observed")?;
249 return Ok(Instruction {
250 peer,
251 endpoint,
252 nonce: word.nonce,
253 });
254 }
255 }
256 pub fn answer_instruction(
260 &mut self,
261 word: &Instruction,
262 index: u32,
263 refresh: bool,
264 ) -> Result<(), PunchError> {
265 self.probe(word.endpoint)?;
266 self.request_punch(word.peer, &word.nonce)?;
267 if refresh {
268 self.carrier()?.refresh_peer(word.peer, word.endpoint)?;
269 } else {
270 self.handshake(word.peer, word.endpoint, index)?;
271 }
272 Ok(())
273 }
274}
275
276fn punch_nonce(opening: bool) -> Result<String, PunchError> {
277 let mut nonce = fresh_nonce()?;
278 if opening {
280 nonce[0] |= 128;
281 } else {
282 nonce[0] &= 127;
283 }
284 Ok(hex::encode(nonce))
285}
286impl Instruction {
287 pub(crate) fn opening(&self) -> bool {
288 matches!(
289 self.nonce.as_bytes().first(),
290 Some(b'8'..=b'9' | b'a'..=b'f' | b'A'..=b'F')
291 )
292 }
293}