1use anyhow::{anyhow, bail, Context, Result};
2use backoff::backoff::Backoff;
3use cloudpub_common::data::DataChannel;
4use cloudpub_common::fair_channel::{fair_channel, FairSender};
5use cloudpub_common::protocol::message::Message;
6use cloudpub_common::protocol::{
7 AgentInfo, ConnectState, Data, DataChannelAck, DataChannelData, DataChannelDataUdp,
8 DataChannelEof, ErrorInfo, ErrorKind, HeartBeat, Protocol,
9};
10use cloudpub_common::transport::{AddrMaybeCached, SocketOpts, Transport, WebsocketTransport};
11use cloudpub_common::utils::{
12 get_platform, proto_to_socket_addr, socket_addr_to_proto, udp_connect,
13};
14use cloudpub_common::VERSION;
15use dashmap::DashMap;
16use parking_lot::RwLock;
17use std::net::SocketAddr;
18use std::sync::Arc;
19use tokio::io::{AsyncReadExt, AsyncWriteExt};
20use tokio::net::{TcpStream, UdpSocket};
21use tokio::sync::mpsc;
22use tokio::time::{self, Duration, Instant};
23use tracing::{debug, error, info, trace, warn};
24
25use cloudpub_common::constants::{
26 run_control_chan_backoff, CONTROL_CHANNEL_SIZE, DATA_BUFFER_SIZE, DATA_CHANNEL_SIZE,
27 DEFAULT_CLIENT_RETRY_INTERVAL_SECS, UDP_BUFFER_SIZE, UDP_TIMEOUT,
28};
29use futures::future::FutureExt;
30
31use crate::config::{ClientConfig, ClientOpts};
32use crate::upgrade::handle_upgrade_available;
33use bytes::Bytes;
34use cloudpub_common::transport::ProtobufStream;
35
36#[cfg(feature = "plugins")]
37use crate::plugins::plugin_trait::PluginHandle;
38#[cfg(feature = "plugins")]
39use crate::plugins::registry::PluginRegistry;
40
41type Service = Arc<DataChannel>;
42
43type Services = Arc<DashMap<String, Service>>;
44
45#[derive(Debug)]
53pub struct ServerRefusal {
54 pub kind: ErrorKind,
55}
56
57impl std::fmt::Display for ServerRefusal {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "Server refused the handshake: {:?}", self.kind)
60 }
61}
62
63impl std::error::Error for ServerRefusal {}
64
65struct RetryPlan {
67 send_synthetic_network_error: bool,
72 retry: bool,
74}
75
76fn plan_retry(err: &anyhow::Error) -> RetryPlan {
79 match err.downcast_ref::<ServerRefusal>() {
80 Some(refusal) => RetryPlan {
81 send_synthetic_network_error: false,
82 retry: !refusal.kind.is_fatal(),
83 },
84 None => RetryPlan {
85 send_synthetic_network_error: true,
86 retry: true,
87 },
88 }
89}
90
91struct Client<T: Transport> {
93 config: Arc<RwLock<ClientConfig>>,
94 opts: ClientOpts,
95 services: Services,
96 transport: Arc<T>,
97 connected: bool,
98 #[cfg(feature = "plugins")]
99 plugin_processes: Arc<DashMap<String, PluginHandle>>,
100 data_channels: Arc<DashMap<u32, Arc<DataChannel>>>,
101}
102
103impl<T: 'static + Transport> Client<T> {
104 async fn from(config: Arc<RwLock<ClientConfig>>, opts: ClientOpts) -> Result<Client<T>> {
106 let transport = Arc::new(
107 T::new(&config.clone().read().transport)
108 .with_context(|| "Failed to create the transport")?,
109 );
110 Ok(Client {
111 config,
112 opts,
113 services: Default::default(),
114 transport,
115 connected: false,
116 #[cfg(feature = "plugins")]
117 plugin_processes: Arc::new(DashMap::new()),
118 data_channels: Arc::new(DashMap::new()),
119 })
120 }
121
122 async fn run(
124 &mut self,
125 mut command_rx: mpsc::Receiver<Message>,
126 result_tx: mpsc::Sender<Message>,
127 ) -> Result<()> {
128 let transport = self.transport.clone();
129
130 let mut retry_backoff = run_control_chan_backoff(DEFAULT_CLIENT_RETRY_INTERVAL_SECS);
131
132 let mut start = Instant::now();
133 result_tx
134 .send(Message::ConnectState(ConnectState::Connecting.into()))
135 .await
136 .context("Can't send Connecting event")?;
137 while let Err(err) = self
138 .run_control_channel(transport.clone(), &mut command_rx, &result_tx)
139 .boxed()
140 .await
141 {
142 if result_tx.is_closed() {
143 break;
145 }
146
147 let plan = plan_retry(&err);
148
149 if self.connected {
150 if plan.send_synthetic_network_error {
151 result_tx
152 .send(Message::Error(ErrorInfo {
153 kind: ErrorKind::HandshakeFailed.into(),
154 message: crate::t!("error-network"),
155 guid: String::new(),
156 }))
157 .await
158 .context("Can't send Error event")?;
159 }
160 result_tx
161 .send(Message::ConnectState(ConnectState::Disconnected.into()))
162 .await
163 .context("Can't send Disconnected event")?;
164 if plan.retry {
165 result_tx
166 .send(Message::ConnectState(ConnectState::Connecting.into()))
167 .await
168 .context("Can't send Connecting event")?;
169 }
170 self.connected = false;
171 }
172
173 self.services.clear();
174 #[cfg(feature = "plugins")]
175 self.plugin_processes.clear();
176 self.data_channels.clear();
177
178 if !plan.retry {
179 error!("Unrecoverable server refusal, stop retrying: {:#}", err);
180 return Err(err);
181 }
182
183 if start.elapsed() > Duration::from_secs(3) {
184 retry_backoff.reset();
186 }
187
188 if let Some(duration) = retry_backoff.next_backoff() {
189 warn!("{:#}. Retry in {:?}...", err, duration);
190 time::sleep(duration).await;
191 }
192
193 start = Instant::now();
194 }
195
196 self.services.clear();
197 #[cfg(feature = "plugins")]
198 self.plugin_processes.clear();
199 self.data_channels.clear();
200
201 Ok(())
202 }
203
204 async fn run_control_channel(
205 &mut self,
206 transport: Arc<T>,
207 command_rx: &mut mpsc::Receiver<Message>,
208 result_tx: &mpsc::Sender<Message>,
209 ) -> Result<()> {
210 let url = self.config.read().server.clone();
211 let port = url.port().unwrap_or(443);
212 let host = url.host_str().context("Failed to get host")?;
213 let mut host_and_port = format!("{}:{}", host, port);
214
215 let (mut conn, _remote_addr) = loop {
216 let mut remote_addr = AddrMaybeCached::new(&host_and_port);
217 remote_addr
218 .resolve()
219 .await
220 .context("Failed to resolve server address")?;
221
222 let mut conn = transport.connect(&remote_addr).await.context(format!(
223 "Failed to connect control channel to {}",
224 host_and_port
225 ))?;
226
227 self.connected = true;
228
229 T::hint(&conn, SocketOpts::for_control_channel());
230
231 let (email, password) = if let Some(ref cred) = self.opts.credentials {
232 (cred.0.clone(), cred.1.clone())
233 } else {
234 (String::new(), String::new())
235 };
236
237 let token = self
238 .config
239 .read()
240 .token
241 .clone()
242 .unwrap_or_default()
243 .to_string();
244
245 let hwid = self.config.read().get_hwid();
246
247 let agent_info = AgentInfo {
248 agent_id: self.config.read().agent_id.clone(),
249 token,
250 email,
251 password,
252 hostname: hostname::get()?.to_string_lossy().into_owned(),
253 version: VERSION.to_string(),
254 gui: self.opts.gui,
255 platform: get_platform(),
256 hwid,
257 server_host_and_port: host_and_port.clone(),
258 branch: cloudpub_common::BRANCH.to_string(),
261 transient: self.opts.transient,
262 secondary: self.opts.secondary,
263 is_service: self.opts.is_service,
264 };
265
266 debug!("Sending hello: {:?}", agent_info);
267
268 let hello_send = Message::AgentHello(agent_info);
269
270 conn.send_message(&hello_send)
271 .await
272 .context("Failed to send hello message")?;
273
274 debug!("Reading ack");
275 match conn
276 .recv_message()
277 .await
278 .context("Failed to read ack message")?
279 {
280 Some(msg) => match msg {
281 Message::AgentAck(args) => {
282 if !args.token.is_empty() {
283 let mut c = self.config.write();
284 c.token = Some(args.token.as_str().into());
285 c.save().context("Write config")?;
286 }
287 break (conn, remote_addr);
288 }
289 Message::Redirect(r) => {
290 host_and_port = r.host_and_port.clone();
291 debug!("Redirecting to {}", host_and_port);
292 continue;
293 }
294 Message::Error(err) => {
295 result_tx
296 .send(Message::Error(err.clone()))
297 .await
298 .context("Can't send server error event")?;
299 return Err(ServerRefusal {
304 kind: err.kind.try_into().unwrap_or(ErrorKind::Fatal),
305 }
306 .into());
307 }
308 v => bail!("Unexpected ack message: {:?}", v),
309 },
310 None => bail!("Connection closed while reading ack message"),
311 };
312 };
313
314 debug!("Control channel established");
315
316 result_tx
317 .send(Message::ConnectState(ConnectState::Connected.into()))
318 .await
319 .context("Can't send Connected event")?;
320
321 let (to_server_tx, mut to_server_rx) = fair_channel::<Message>(CONTROL_CHANNEL_SIZE);
322 let heartbeat_timeout = self.config.read().heartbeat_timeout;
325
326 loop {
327 tokio::select! {
328 cmd = to_server_rx.recv() => {
329 if let Some(cmd) = cmd {
330 conn.send_message(&cmd).await.context("Failed to send command")?;
331 }
332 },
333 cmd = command_rx.recv() => {
334 if let Some(cmd) = cmd {
335 debug!("Received message: {:?}", cmd);
336 match cmd {
337 Message::PerformUpgrade(info) => {
338 let config_clone = self.config.clone();
339 if let Err(e) = handle_upgrade_available(
340 &info.version,
341 config_clone,
342 self.opts.gui,
343 command_rx,
344 result_tx,
345 )
346 .await
347 {
348 result_tx.send(Message::Error(ErrorInfo {
349 kind: ErrorKind::Fatal.into(),
350 message: e.to_string(),
351 guid: String::new(),
352 }))
353 .await
354 .context("Can't send Error event")?;
355 }
356 }
357 Message::Stop(_x) => {
358 info!("Stopping the client");
359 break;
360 }
361 Message::Break(break_msg) => {
362 info!("Breaking operation for guid: {}", break_msg.guid);
363 #[cfg(feature = "plugins")]
364 if let Some((_, handle)) = self.plugin_processes.remove(&break_msg.guid) {
365 info!("Dropped plugin handle for guid: {}", break_msg.guid);
366 drop(handle);
367 }
368 }
369 cmd => {
370 conn.send_message(&cmd).await.context("Failed to send message")?;
371 }
372 };
373 } else {
374 debug!("No more commands, shutting down...");
375 break;
376 }
377 },
378 val = conn.recv_message() => {
379 match val? {
380 Some(val) => {
381 match val {
382 Message::EndpointAck(mut endpoint) => {
383 if !is_valid_guid(&endpoint.guid) {
390 warn!(
391 "Rejecting EndpointAck with unsafe guid: {:?}",
392 endpoint.guid
393 );
394 continue;
395 }
396 #[cfg(feature = "plugins")]
397 {
398 let to_server_tx = to_server_tx.clone();
399 let config = self.config.clone();
400 let opts = self.opts.clone();
401 if endpoint.error.is_empty() {
402 let protocol: Protocol = endpoint
403 .client
404 .as_ref()
405 .unwrap()
406 .local_proto
407 .try_into()
408 .unwrap_or(Protocol::Tcp);
409 if let Some(plugin) = PluginRegistry::new().get(protocol) {
410 let already_running = self
416 .plugin_processes
417 .get(&endpoint.guid)
418 .map(|h| h.same_config(&endpoint) && h.is_healthy())
419 .unwrap_or(false);
420 if already_running {
421 debug!(
422 "Endpoint {} already running with the same config, \
423 ignoring duplicate start",
424 endpoint.guid
425 );
426 } else {
427 let handle = PluginHandle::spawn(
428 plugin,
429 endpoint.clone(),
430 config,
431 opts,
432 to_server_tx,
433 );
434 self.plugin_processes.insert(endpoint.guid.clone(), handle);
435 }
436 } else {
437 endpoint.status = Some("online".into());
438 let _ = to_server_tx.send(Message::EndpointStatus(endpoint.clone())).await;
439 }
440 }
441 }
442 #[cfg(not(feature = "plugins"))]
443 if endpoint.error.is_empty() {
444 endpoint.status = Some("online".into());
449 let _ = to_server_tx.send(Message::EndpointStatus(endpoint.clone())).await;
450 }
451 result_tx
452 .send(Message::EndpointAck(endpoint))
453 .await
454 .context("Can't send EndpointAck event")?;
455 }
456
457 Message::CreateDataChannelWithId(create_msg) => {
458 let channel_id = create_msg.channel_id;
459 let endpoint = create_msg.endpoint.unwrap();
460
461 trace!("Creating data channel {} for endpoint {:?}", channel_id, endpoint.guid);
462
463 let (to_service_tx, to_service_rx) = mpsc::channel::<Data>(DATA_CHANNEL_SIZE);
465
466 let data_channel = Arc::new(DataChannel::new_client(channel_id, to_service_tx.clone()));
468 self.data_channels.insert(channel_id, data_channel.clone());
469
470 let client = endpoint.client.unwrap();
472 #[allow(unused_mut)]
473 let mut local_addr = format!("{}:{}", client.local_addr, client.local_port);
474 #[cfg(feature = "plugins")]
475 if let Some(handle) = self.plugin_processes.get(&endpoint.guid) {
476 if let Some(port) = handle.value().port() {
477 local_addr = format!("127.0.0.1:{}", port);
478 }
479 }
480
481 let data_channels = self.data_channels.clone();
483 let protocol: Protocol = client.local_proto.try_into().unwrap();
484
485 let to_server_tx_cloned = to_server_tx.clone();
486 tokio::spawn(async move {
487 if let Err(err) = if protocol == Protocol::Udp {
488 handle_udp_data_channel(
489 data_channel,
490 local_addr,
491 to_server_tx_cloned.clone(),
492 to_service_rx
493 ).await
494 } else {
495 handle_tcp_data_channel(
496 data_channel,
497 local_addr,
498 to_server_tx_cloned.clone(),
499 to_service_rx
500 ).await
501 } {
502 error!("DataChannel {{ channel_id: {} }}: {:?}", channel_id, err);
503 to_server_tx_cloned
504 .send(Message::DataChannelEof(
505 DataChannelEof {
506 channel_id,
507 error: err.to_string()
508 })
509 ).await.ok();
510 }
511 if let Some((_, dc)) = data_channels.remove(&channel_id) { dc.close() }
512 });
513 },
514
515 Message::DataChannelData(data) => {
516 let to_service_tx = self.data_channels.get(&data.channel_id).map(|ch| ch.data_tx.clone());
518 if let Some(tx) = to_service_tx {
519 if let Err(err) = tx.send(Data {
520 data: data.data.into(),
521 socket_addr: None
522 }).await {
523 self.data_channels.remove(&data.channel_id);
524 error!("Error send to data channel {}: {:?}", data.channel_id, err);
525 }
526 } else {
527 trace!("Data channel {} not found, dropping data", data.channel_id);
528 }
529 },
530
531 Message::DataChannelDataUdp(data) => {
532 let to_service_tx = self.data_channels.get(&data.channel_id).map(|ch| ch.data_tx.clone());
534 if let Some(tx) = to_service_tx {
535 let socket_addr = data.socket_addr.as_ref()
536 .map(proto_to_socket_addr)
537 .transpose()
538 .unwrap_or_else(|err| {
539 error!("Invalid socket address for UDP data channel {}: {:?}", data.channel_id, err);
540 None
541 });
542
543 if let Err(err) = tx.send(Data {
544 data: data.data.into(),
545 socket_addr,
546 }).await {
547 self.data_channels.remove(&data.channel_id);
548 error!("Error send to UDP data channel {}: {:?}", data.channel_id, err);
549 }
550 } else {
551 trace!("UDP Data channel {} not found, dropping data", data.channel_id);
552 }
553 },
554
555 Message::DataChannelEof(eof) => {
556 if let Some((_, dc)) = self.data_channels.remove(&eof.channel_id) { dc.close() }
558 if eof.error.is_empty() {
559 trace!("Data channel {} closed by server", eof.channel_id);
561 } else {
562 trace!("Data channel {} closed by server with error: {}", eof.channel_id, eof.error);
564 }
565 },
566
567 Message::DataChannelAck(DataChannelAck { channel_id, consumed }) => {
568 if let Some(ch) = self.data_channels.get(&channel_id) {
569 ch.add_capacity(consumed);
570 }
571 }
572
573 Message::EndpointStopAck(ref ep) => {
574 self.services.remove(&ep.guid);
575 #[cfg(feature = "plugins")]
576 self.plugin_processes.remove(&ep.guid);
577 result_tx.send(val).await.context("Can't send result message")?;
578 }
579
580 Message::EndpointRemoveAck(ref ep) => {
581 self.services.remove(&ep.guid);
582 #[cfg(feature = "plugins")]
583 self.plugin_processes.remove(&ep.guid);
584 result_tx.send(val).await.context("Can't send result message")?;
585 }
586
587 Message::HeartBeat(_) => {
588 conn.send_message(&Message::HeartBeat(HeartBeat{})).await.context("Failed to send heartbeat")?;
589 },
590
591 Message::Error(ref err) => {
592 let kind: ErrorKind = err.kind.try_into().unwrap_or(ErrorKind::Fatal);
593 result_tx.send(val.clone()).await.context("Can't send result message")?;
594 if kind.is_fatal() {
595 error!("Fatal error received, stop client: {:?}", err);
596 break;
597 }
598 }
599
600 Message::Break(break_msg) => {
601 info!("Breaking operation for guid: {}", break_msg.guid);
602 #[cfg(feature = "plugins")]
603 self.plugin_processes.remove(&break_msg.guid);
604 }
605
606 Message::PerformUpgrade(info) => {
607 let config_clone = self.config.clone();
608 #[cfg(feature = "plugins")]
609 self.plugin_processes.clear();
610 self.services.clear();
611 self.data_channels.clear();
612
613 if let Err(e) = handle_upgrade_available(
614 &info.version,
615 config_clone,
616 self.opts.gui,
617 command_rx,
618 result_tx,
619 )
620 .await
621 {
622 conn.send_message(&Message::Error(ErrorInfo {
623 kind: ErrorKind::UpgradeFailed.into(),
624 message: e.to_string(),
625 guid: String::new(),
626 }))
627 .await
628 .context("Can't send Error event")?;
629 }
630 }
631
632 v => {
633 result_tx.send(v).await.context("Can't send result message")?;
634 }
635 }
636 },
637 None => {
638 debug!("Connection closed by server");
639 break;
640 }
641 }
642 },
643 _ = time::sleep(Duration::from_secs(heartbeat_timeout)), if heartbeat_timeout != 0 => {
644 return Err(anyhow!("Heartbeat timed out"))
645 }
646 }
647 }
648
649 info!("Control channel shutdown");
650 result_tx
651 .send(Message::ConnectState(ConnectState::Disconnected.into()))
652 .await
653 .context("Can't send Disconnected event")?;
654 conn.close().await.ok();
655 time::sleep(Duration::from_millis(100)).await; Ok(())
657 }
658}
659
660pub async fn run_client(
661 config: Arc<RwLock<ClientConfig>>,
662 opts: ClientOpts,
663 command_rx: mpsc::Receiver<Message>,
664 result_tx: mpsc::Sender<Message>,
665) -> Result<()> {
666 let mut client = Client::<WebsocketTransport>::from(config, opts)
667 .await
668 .context("Failed to create Websocket client")?;
669 client.run(command_rx, result_tx).await
670}
671async fn handle_tcp_data_channel(
672 data_channel: Arc<DataChannel>,
673 local_addr: String,
674 to_server_tx: FairSender<Message>,
675 mut data_rx: mpsc::Receiver<Data>,
676) -> Result<()> {
677 trace!("Handling client {:?} to {}", data_channel, local_addr);
678
679 let mut local_stream = TcpStream::connect(&local_addr)
681 .await
682 .with_context(|| format!("Failed to connect to local service at {}", local_addr))?;
683
684 local_stream
686 .set_nodelay(true)
687 .context("Failed to set TCP_NODELAY")?;
688
689 let mut buf = [0u8; DATA_BUFFER_SIZE]; loop {
692 tokio::select! {
693 res = local_stream.read(&mut buf) => {
694 match res {
695 Ok(0) => {
696 trace!("EOF received from local service for {:?}", data_channel);
697 if let Err(err) = to_server_tx.send(Message::DataChannelEof(DataChannelEof {
698 channel_id: data_channel.id,
699 error: String::new()
700 }))
701 .await {
702 trace!("Failed to send EOF to server for {:?}: {:#}", data_channel, err);
703 }
704 break;
705 },
706 Ok(n) => {
707 if data_channel.wait_for_capacity(n as u32).await.is_err() {
709 trace!("Data channel {} closed when waiting for capacity", data_channel.id);
710 break;
711 }
712 if let Err(err) = to_server_tx.send(Message::DataChannelData(DataChannelData {
713 channel_id: data_channel.id,
714 data: buf[0..n].to_vec()
715 }))
716 .await {
717 trace!("Failed to send data to server for {:?}: {:#}", data_channel, err);
718 break;
719 }
720 },
721 Err(e) => {
722 return Err(e).context("Failed to read from local service");
723 }
724 }
725 }
726
727 data_result = data_rx.recv() => {
729 match data_result {
730 Some(data) => {
731 trace!("Received {} bytes from server for {:?}", data.data.len(), data_channel);
732 local_stream.write_all(&data.data).await.context("Failed to write data to local service")?;
733 to_server_tx.send(Message::DataChannelAck(
734 DataChannelAck {
735 channel_id: data_channel.id,
736 consumed: data.data.len() as u32
737 }
738 )).await.with_context(|| "Failed to send TCP traffic ack to the server")?;
739 },
740 None => {
741 trace!("EOF received from server for {:?}", data_channel);
742 break;
743 }
744 }
745 }
746
747 _ = data_channel.closed() => {
748 trace!("Data channel {} closed", data_channel.id);
749 break;
750 }
751 }
752 }
753 Ok(())
754}
755
756type UdpPortMap = Arc<DashMap<SocketAddr, mpsc::Sender<Bytes>>>;
758
759async fn handle_udp_data_channel(
760 data_channel: Arc<DataChannel>,
761 local_addr: String,
762 to_server_tx: FairSender<Message>,
763 mut data_rx: mpsc::Receiver<Data>,
764) -> Result<()> {
765 trace!(
766 "Handling client UDP channel {:?} to {}",
767 data_channel,
768 local_addr
769 );
770
771 let port_map: UdpPortMap = Arc::new(DashMap::new());
772
773 loop {
774 let data_channel = data_channel.clone();
775 tokio::select! {
777 data = data_rx.recv() => {
778 match data {
779 Some(data) => {
780 let external_addr = data.socket_addr.unwrap();
781
782 if !port_map.contains_key(&external_addr) {
783 match udp_connect(&local_addr).await {
788 Ok(s) => {
789 let (to_service_tx, to_service_rx) = mpsc::channel(DATA_CHANNEL_SIZE);
790 port_map.insert(external_addr, to_service_tx);
791 tokio::spawn(run_udp_forwarder(
792 s,
793 to_service_rx,
794 to_server_tx.clone(),
795 external_addr,
796 data_channel,
797 port_map.clone(),
798 ));
799 }
800 Err(e) => {
801 error!(
802 "Failed to create UDP forwarder for {}: {:#}",
803 external_addr, e
804 );
805 }
806 }
807 }
808
809 if let Some(tx) = port_map.get(&external_addr) {
811 let _ = tx.send(data.data).await;
812 }
813 }
814 None => {
815 trace!("EOF received from server for UDP {:?}", data_channel);
816 break;
817 }
818 }
819 }
820 _ = data_channel.closed() => {
821 trace!("Data channel {} closed", data_channel.id);
822 break;
823 }
824 }
825 }
826 Ok(())
827}
828
829async fn run_udp_forwarder(
831 s: UdpSocket,
832 mut to_service_rx: mpsc::Receiver<Bytes>,
833 to_server_tx: FairSender<Message>,
834 from: SocketAddr,
835 data_channel: Arc<DataChannel>,
836 port_map: UdpPortMap,
837) -> Result<()> {
838 trace!("UDP forwarder created for {} on {:?}", from, data_channel);
839 let mut buf = vec![0u8; UDP_BUFFER_SIZE];
840
841 loop {
842 tokio::select! {
843 data = to_service_rx.recv() => {
845 if let Some(data) = data {
846 s.send(&data).await.with_context(|| "Failed to send UDP traffic to the service")?;
847 to_server_tx.send(Message::DataChannelAck(
848 DataChannelAck {
849 channel_id: data_channel.id,
850 consumed: data.len() as u32
851 }
852 )).await.with_context(|| "Failed to send UDP traffic ack to the server")?;
853 } else {
854 break;
855 }
856 },
857
858 val = s.recv(&mut buf) => {
860 let len = match val {
861 Ok(v) => v,
862 Err(_) => break
863 };
864
865 if data_channel.wait_for_capacity(len as u32).await.is_err() {
866 break;
867 }
868
869 to_server_tx.send(Message::DataChannelDataUdp(
870 DataChannelDataUdp {
871 channel_id: data_channel.id,
872 data: buf[..len].to_vec(),
873 socket_addr: Some(socket_addr_to_proto(&from)),
874 })).await.with_context(|| "Failed to send UDP traffic to the server")?;
875 },
876
877 _ = time::sleep(Duration::from_secs(UDP_TIMEOUT)) => {
879 break;
880 }
881
882 _ = data_channel.closed() => {
883 trace!("Data channel {} closed", data_channel.id);
884 break;
885 }
886 }
887 }
888
889 port_map.remove(&from);
890
891 debug!("UDP forwarder dropped for {} on {:?}", from, data_channel);
892 Ok(())
893}
894
895fn is_valid_guid(guid: &str) -> bool {
901 !guid.is_empty()
902 && guid
903 .bytes()
904 .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
905}
906
907#[cfg(test)]
908mod tests {
909 use super::*;
910
911 #[test]
921 fn test_endpoint_ack_rejects_path_traversal_guid() {
922 assert!(is_valid_guid("550e8400-e29b-41d4-a716-446655440000"));
924 assert!(is_valid_guid("abc_DEF-123"));
925
926 assert!(!is_valid_guid("../evil"));
928 assert!(!is_valid_guid("a/b"));
929 assert!(!is_valid_guid("a\\b"));
930 assert!(!is_valid_guid(".."));
931 assert!(!is_valid_guid("."));
932 assert!(!is_valid_guid(""));
933 assert!(!is_valid_guid("x/../../etc/cron.d/job"));
934 assert!(!is_valid_guid("null\0byte"));
935 }
936
937 #[test]
955 fn fatal_server_refusal_stops_retrying_without_masking() {
956 let err: anyhow::Error = ServerRefusal {
957 kind: ErrorKind::Fatal,
958 }
959 .into();
960 let plan = plan_retry(&err);
961 assert!(
962 !plan.retry,
963 "a fatal server refusal can never succeed by retrying"
964 );
965 assert!(
966 !plan.send_synthetic_network_error,
967 "the server's own message must stay the last one the GUI sees"
968 );
969 }
970
971 #[test]
972 fn non_fatal_server_refusal_still_retries_without_masking() {
973 let err: anyhow::Error = ServerRefusal {
976 kind: ErrorKind::HandshakeFailed,
977 }
978 .into();
979 let plan = plan_retry(&err);
980 assert!(plan.retry, "transient refusals must keep retrying");
981 assert!(
982 !plan.send_synthetic_network_error,
983 "the forwarded server message must not be overwritten by the \
984 synthetic network error"
985 );
986 }
987
988 #[test]
989 fn lease_busy_refusal_still_retries() {
990 let err: anyhow::Error = ServerRefusal {
993 kind: ErrorKind::LeaseBusy,
994 }
995 .into();
996 assert!(plan_retry(&err).retry);
997 }
998
999 #[test]
1000 fn plain_network_failure_keeps_synthetic_error_and_retry() {
1001 let err = anyhow::anyhow!("Failed to connect control channel to example.com:443");
1004 let plan = plan_retry(&err);
1005 assert!(plan.retry);
1006 assert!(plan.send_synthetic_network_error);
1007 }
1008
1009 #[test]
1010 fn server_refusal_is_detected_through_context_wrapping() {
1011 let err = anyhow::Error::new(ServerRefusal {
1013 kind: ErrorKind::Fatal,
1014 })
1015 .context("run_control_channel failed");
1016 assert!(!plan_retry(&err).retry);
1017 }
1018}