liminal_server/server/
link_udp_endpoint.rs1use crate::link_handshake::MachineIdentity;
3use crate::link_open::network::Network;
4use crate::link_open::{OpenError, Stop};
5use crate::link_udp::{UdpCarrier, streams::Inbox};
6use crate::server::connection::{
7 ConnectionSupervisor, LiminalConnectionServices, LoopbackClientEnd, LoopbackDuplex,
8};
9use liminal::protocol::{Frame, FrameHeader, LinkEnd, codec};
10use std::collections::BTreeMap;
11use std::io::Write;
12use std::sync::Arc;
13use std::thread::JoinHandle;
14use zeroize::Zeroizing;
15
16type Worker = JoinHandle<Result<(), OpenError>>;
17pub struct UdpEndpoint {
19 stop: Stop,
20 worker: Option<JoinHandle<Result<Vec<Worker>, OpenError>>>,
21}
22impl std::fmt::Debug for UdpEndpoint {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.debug_struct("UdpEndpoint").finish_non_exhaustive()
25 }
26}
27impl UdpEndpoint {
28 pub(crate) fn start(
29 network: Arc<Network>,
30 supervisor: ConnectionSupervisor,
31 services: Arc<LiminalConnectionServices>,
32 ) -> Result<Self, OpenError> {
33 let (mut puncher, connections) = network.incoming()?;
34 let stop = network.stop_handle();
35 let worker = std::thread::Builder::new()
36 .name("link-udp-accept".to_owned())
37 .spawn(move || {
38 let mut peers = BTreeMap::new();
39 let mut workers = Vec::new();
40 loop {
41 let instruction = match puncher.next_instruction() {
42 Ok(word) => word,
43 Err(error) => {
44 if !network.is_stopped() {
45 tracing::warn!(%error, "broker instruction stream ended; keyed UDP imports remain served");
46 }
47 break;
48 }
49 };
50 let peer = instruction.peer;
51 let key = hex::encode(peer.ed25519_public());
52 let existing = peers.get(&key).copied();
53 let (index,refresh) = match network.accept_instruction(&mut puncher, &instruction, existing) {
54 Ok(Some(index)) => index,
55 Ok(None) => continue,
56 Err(error) => {
57 tracing::warn!(%error, "incoming punch refused");
58 continue;
59 }
60 };
61 if refresh { continue; }
62 let carrier = puncher.shared_carrier().map_err(named)?;
63 carrier.watch_acknowledgements(index).map_err(named)?;
64 let inbox = carrier.subscribe(index).map_err(named)?;
65 let supervisor = supervisor.clone();
66 let services = services.clone();
67 workers.push(std::thread::Builder::new()
68 .name("link-udp-import".to_owned())
69 .spawn(move || serve(&carrier, index, peer, &inbox, &supervisor, &services))
70 .map_err(named)?);
71 peers.insert(key, index);
72 }
73 drop(connections);
74 Ok(workers)
75 }).map_err(named)?;
76 Ok(Self {
77 stop,
78 worker: Some(worker),
79 })
80 }
81 pub(crate) fn finish(&mut self) -> Result<(), OpenError> {
82 let stopped = (self.stop)();
83 let Some(worker) = self.worker.take() else {
84 return stopped;
85 };
86 let workers = worker
87 .join()
88 .map_err(|_| OpenError("udp_accept_worker_panicked".to_owned()))??;
89 let mut failure = stopped.err();
90 for worker in workers {
91 match worker.join() {
92 Ok(Ok(())) => {}
93 Ok(Err(error)) => {
94 tracing::error!(%error,"UDP importer ended by refusal");
95 if failure.is_none() {
96 failure = Some(error);
97 }
98 }
99 Err(_) => {
100 failure = Some(OpenError("udp_import_worker_panicked".to_owned()));
101 }
102 }
103 }
104 failure.map_or(Ok(()), Err)
105 }
106}
107impl Drop for UdpEndpoint {
108 fn drop(&mut self) {
109 if self.worker.is_some()
110 && let Err(error) = self.finish()
111 {
112 tracing::error!(%error,"UDP endpoint shutdown failed");
113 }
114 }
115}
116fn named(error: impl std::fmt::Display) -> OpenError {
117 OpenError(error.to_string())
118}
119
120fn serve(
121 carrier: &UdpCarrier,
122 index: u32,
123 peer: MachineIdentity,
124 inbox: &Inbox,
125 supervisor: &ConnectionSupervisor,
126 services: &LiminalConnectionServices,
127) -> Result<(), OpenError> {
128 let (mut connection, server) = LoopbackDuplex::bounded(crate::link_carrier::WIRE_FRAME_BOUND);
129 supervisor
130 .spawn_loopback_connection(server)
131 .map_err(named)?;
132 let mut assembled = Zeroizing::new(Vec::new());
133 let mut total = 0;
134 let mut connected = false;
135 let mut opened = false;
136 loop {
137 let datagram = match inbox.receive() {
138 Ok(datagram) => datagram,
139 Err(crate::link_udp::UdpError::Stopped) => return Ok(()),
140 Err(error) => return Err(named(error)),
141 };
142 let payload = &datagram.payload;
143 if payload.get(..4) != Some(b"LMUC") {
144 return Err(OpenError("invalid_udp_request".to_owned()));
145 }
146 let length = read_u32(payload.get(4..8))?;
147 let offset = read_u32(payload.get(8..12))?;
148 let body = payload
149 .get(12..)
150 .ok_or_else(|| OpenError("short_udp_request".to_owned()))?;
151 if length == 0
152 || length > crate::link_carrier::WIRE_FRAME_BOUND
153 || offset != assembled.len()
154 || body.is_empty()
155 || body.len() > length.saturating_sub(offset)
156 {
157 return Err(OpenError("invalid_udp_request_extent".to_owned()));
158 }
159 if offset == 0 {
160 total = length;
161 } else if total != length {
162 return Err(OpenError("udp_request_length_changed".to_owned()));
163 }
164 assembled.extend_from_slice(body);
165 if assembled.len() != total {
166 continue;
167 }
168 let (frame, read) = codec::decode(&assembled)
169 .map_err(|_| OpenError("invalid_udp_application_frame".to_owned()))?;
170 if read != assembled.len() {
171 return Err(OpenError("udp_request_has_trailing_bytes".to_owned()));
172 }
173 match &frame {
174 Frame::Connect { .. } if !connected => connected = true,
175 Frame::LinkOpen {
176 stream_id: 1,
177 offer,
178 ..
179 } if connected
180 && !opened
181 && offer.machine == *peer.ed25519_public()
182 && offer.end == LinkEnd::Export =>
183 {
184 opened = true;
185 }
186 Frame::LinkMessage { stream_id: 1, .. } if opened => {}
187 _ => return Err(OpenError("udp_frame_not_admitted_on_link".to_owned())),
188 }
189 connection.write_all(&assembled).map_err(named)?;
190 assembled.clear();
191 total = 0;
192 let response = read_frame(&mut connection)?;
194 let mut reply = b"LMUR".to_vec();
195 reply.extend_from_slice(&datagram.counter.to_be_bytes());
196 reply.extend(response);
197 carrier
198 .reply(index, datagram.counter, &reply)
199 .map_err(named)?;
200 services
201 .console()
202 .emit(crate::console::TopologyChange::Link {
203 link_id: crate::console::records::Link::to_peer(
204 services.console().device(),
205 &hex::encode(peer.ed25519_public()),
206 )
207 .id,
208 peer: hex::encode(peer.ed25519_public()),
209 carrier: Some(crate::console::records::Carrier::Direct),
210 state: crate::console::records::LinkState::Up,
211 reason: None,
212 counters: crate::console::records::LinkCounters::default(),
213 })
214 .map_err(named)?;
215 }
216}
217fn read_u32(bytes: Option<&[u8]>) -> Result<usize, OpenError> {
218 usize::try_from(u32::from_be_bytes(
219 bytes
220 .ok_or_else(|| OpenError("short_udp_request".to_owned()))?
221 .try_into()
222 .map_err(|_| OpenError("short_udp_request".to_owned()))?,
223 ))
224 .map_err(named)
225}
226fn read_frame(connection: &mut LoopbackClientEnd) -> Result<Vec<u8>, OpenError> {
227 let mut header = [0; FrameHeader::WIRE_LEN];
228 read_exact(connection, &mut header)?;
229 let length = usize::try_from(u32::from_be_bytes(header[6..10].try_into().map_err(named)?))
230 .map_err(named)?;
231 if length > crate::link_carrier::WIRE_FRAME_BOUND {
232 return Err(OpenError("udp_route_response_too_large".to_owned()));
233 }
234 let mut bytes = header.to_vec();
235 bytes.resize(header.len() + length, 0);
236 read_exact(connection, &mut bytes[header.len()..])?;
237 Ok(bytes)
238}
239fn read_exact(connection: &mut LoopbackClientEnd, mut bytes: &mut [u8]) -> Result<(), OpenError> {
240 while !bytes.is_empty() {
241 let read = connection.read_timeout(bytes, None).map_err(named)?;
242 if read == 0 {
243 return Err(OpenError("udp_route_ended_before_answer".to_owned()));
244 }
245 bytes = &mut bytes[read..];
246 }
247 Ok(())
248}