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