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
45struct Client<T: Transport> {
47 config: Arc<RwLock<ClientConfig>>,
48 opts: ClientOpts,
49 services: Services,
50 transport: Arc<T>,
51 connected: bool,
52 #[cfg(feature = "plugins")]
53 plugin_processes: Arc<DashMap<String, PluginHandle>>,
54 data_channels: Arc<DashMap<u32, Arc<DataChannel>>>,
55}
56
57impl<T: 'static + Transport> Client<T> {
58 async fn from(config: Arc<RwLock<ClientConfig>>, opts: ClientOpts) -> Result<Client<T>> {
60 let transport = Arc::new(
61 T::new(&config.clone().read().transport)
62 .with_context(|| "Failed to create the transport")?,
63 );
64 Ok(Client {
65 config,
66 opts,
67 services: Default::default(),
68 transport,
69 connected: false,
70 #[cfg(feature = "plugins")]
71 plugin_processes: Arc::new(DashMap::new()),
72 data_channels: Arc::new(DashMap::new()),
73 })
74 }
75
76 async fn run(
78 &mut self,
79 mut command_rx: mpsc::Receiver<Message>,
80 result_tx: mpsc::Sender<Message>,
81 ) -> Result<()> {
82 let transport = self.transport.clone();
83
84 let mut retry_backoff = run_control_chan_backoff(DEFAULT_CLIENT_RETRY_INTERVAL_SECS);
85
86 let mut start = Instant::now();
87 result_tx
88 .send(Message::ConnectState(ConnectState::Connecting.into()))
89 .await
90 .context("Can't send Connecting event")?;
91 while let Err(err) = self
92 .run_control_channel(transport.clone(), &mut command_rx, &result_tx)
93 .boxed()
94 .await
95 {
96 if result_tx.is_closed() {
97 break;
99 }
100
101 if self.connected {
102 result_tx
103 .send(Message::Error(ErrorInfo {
104 kind: ErrorKind::HandshakeFailed.into(),
105 message: crate::t!("error-network"),
106 guid: String::new(),
107 }))
108 .await
109 .context("Can't send Error event")?;
110 result_tx
111 .send(Message::ConnectState(ConnectState::Disconnected.into()))
112 .await
113 .context("Can't send Disconnected event")?;
114 result_tx
115 .send(Message::ConnectState(ConnectState::Connecting.into()))
116 .await
117 .context("Can't send Connecting event")?;
118 self.connected = false;
119 }
120
121 self.services.clear();
122 #[cfg(feature = "plugins")]
123 self.plugin_processes.clear();
124 self.data_channels.clear();
125
126 if start.elapsed() > Duration::from_secs(3) {
127 retry_backoff.reset();
129 }
130
131 if let Some(duration) = retry_backoff.next_backoff() {
132 warn!("{:#}. Retry in {:?}...", err, duration);
133 time::sleep(duration).await;
134 }
135
136 start = Instant::now();
137 }
138
139 self.services.clear();
140 #[cfg(feature = "plugins")]
141 self.plugin_processes.clear();
142 self.data_channels.clear();
143
144 Ok(())
145 }
146
147 async fn run_control_channel(
148 &mut self,
149 transport: Arc<T>,
150 command_rx: &mut mpsc::Receiver<Message>,
151 result_tx: &mpsc::Sender<Message>,
152 ) -> Result<()> {
153 let url = self.config.read().server.clone();
154 let port = url.port().unwrap_or(443);
155 let host = url.host_str().context("Failed to get host")?;
156 let mut host_and_port = format!("{}:{}", host, port);
157
158 let (mut conn, _remote_addr) = loop {
159 let mut remote_addr = AddrMaybeCached::new(&host_and_port);
160 remote_addr
161 .resolve()
162 .await
163 .context("Failed to resolve server address")?;
164
165 let mut conn = transport.connect(&remote_addr).await.context(format!(
166 "Failed to connect control channel to {}",
167 &host_and_port
168 ))?;
169
170 self.connected = true;
171
172 T::hint(&conn, SocketOpts::for_control_channel());
173
174 let (email, password) = if let Some(ref cred) = self.opts.credentials {
175 (cred.0.clone(), cred.1.clone())
176 } else {
177 (String::new(), String::new())
178 };
179
180 let token = self
181 .config
182 .read()
183 .token
184 .clone()
185 .unwrap_or_default()
186 .to_string();
187
188 let hwid = self.config.read().get_hwid();
189
190 let agent_info = AgentInfo {
191 agent_id: self.config.read().agent_id.clone(),
192 token,
193 email,
194 password,
195 hostname: hostname::get()?.to_string_lossy().into_owned(),
196 version: VERSION.to_string(),
197 gui: self.opts.gui,
198 platform: get_platform(),
199 hwid,
200 server_host_and_port: host_and_port.clone(),
201 transient: self.opts.transient,
202 secondary: self.opts.secondary,
203 is_service: self.opts.is_service,
204 };
205
206 debug!("Sending hello: {:?}", agent_info);
207
208 let hello_send = Message::AgentHello(agent_info);
209
210 conn.send_message(&hello_send)
211 .await
212 .context("Failed to send hello message")?;
213
214 debug!("Reading ack");
215 match conn
216 .recv_message()
217 .await
218 .context("Failed to read ack message")?
219 {
220 Some(msg) => match msg {
221 Message::AgentAck(args) => {
222 if !args.token.is_empty() {
223 let mut c = self.config.write();
224 c.token = Some(args.token.as_str().into());
225 c.save().context("Write config")?;
226 }
227 break (conn, remote_addr);
228 }
229 Message::Redirect(r) => {
230 host_and_port = r.host_and_port.clone();
231 debug!("Redirecting to {}", host_and_port);
232 continue;
233 }
234 Message::Error(err) => {
235 result_tx
236 .send(Message::Error(err.clone()))
237 .await
238 .context("Can't send server error event")?;
239 bail!("Error: {:?}", err.kind);
240 }
241 v => bail!("Unexpected ack message: {:?}", v),
242 },
243 None => bail!("Connection closed while reading ack message"),
244 };
245 };
246
247 debug!("Control channel established");
248
249 result_tx
250 .send(Message::ConnectState(ConnectState::Connected.into()))
251 .await
252 .context("Can't send Connected event")?;
253
254 let (to_server_tx, mut to_server_rx) = fair_channel::<Message>(CONTROL_CHANNEL_SIZE);
255 let heartbeat_timeout = self.config.read().heartbeat_timeout;
258
259 loop {
260 tokio::select! {
261 cmd = to_server_rx.recv() => {
262 if let Some(cmd) = cmd {
263 conn.send_message(&cmd).await.context("Failed to send command")?;
264 }
265 },
266 cmd = command_rx.recv() => {
267 if let Some(cmd) = cmd {
268 debug!("Received message: {:?}", cmd);
269 match cmd {
270 Message::PerformUpgrade(info) => {
271 let config_clone = self.config.clone();
272 if let Err(e) = handle_upgrade_available(
273 &info.version,
274 config_clone,
275 self.opts.gui,
276 command_rx,
277 result_tx,
278 )
279 .await
280 {
281 result_tx.send(Message::Error(ErrorInfo {
282 kind: ErrorKind::Fatal.into(),
283 message: e.to_string(),
284 guid: String::new(),
285 }))
286 .await
287 .context("Can't send Error event")?;
288 }
289 }
290 Message::Stop(_x) => {
291 info!("Stopping the client");
292 break;
293 }
294 Message::Break(break_msg) => {
295 info!("Breaking operation for guid: {}", break_msg.guid);
296 #[cfg(feature = "plugins")]
297 if let Some((_, handle)) = self.plugin_processes.remove(&break_msg.guid) {
298 info!("Dropped plugin handle for guid: {}", break_msg.guid);
299 drop(handle);
300 }
301 }
302 cmd => {
303 conn.send_message(&cmd).await.context("Failed to send message")?;
304 }
305 };
306 } else {
307 debug!("No more commands, shutting down...");
308 break;
309 }
310 },
311 val = conn.recv_message() => {
312 match val? {
313 Some(val) => {
314 match val {
315 Message::EndpointAck(mut endpoint) => {
316 #[cfg(feature = "plugins")]
317 {
318 let to_server_tx = to_server_tx.clone();
319 let config = self.config.clone();
320 let opts = self.opts.clone();
321 if endpoint.error.is_empty() {
322 let protocol: Protocol = endpoint
323 .client
324 .as_ref()
325 .unwrap()
326 .local_proto
327 .try_into()
328 .unwrap_or(Protocol::Tcp);
329 if let Some(plugin) = PluginRegistry::new().get(protocol) {
330 let already_running = self
336 .plugin_processes
337 .get(&endpoint.guid)
338 .map(|h| h.same_config(&endpoint) && h.is_healthy())
339 .unwrap_or(false);
340 if already_running {
341 debug!(
342 "Endpoint {} already running with the same config, \
343 ignoring duplicate start",
344 endpoint.guid
345 );
346 } else {
347 let handle = PluginHandle::spawn(
348 plugin,
349 endpoint.clone(),
350 config,
351 opts,
352 to_server_tx,
353 );
354 self.plugin_processes.insert(endpoint.guid.clone(), handle);
355 }
356 } else {
357 endpoint.status = Some("online".into());
358 let _ = to_server_tx.send(Message::EndpointStatus(endpoint.clone())).await;
359 }
360 }
361 }
362 #[cfg(not(feature = "plugins"))]
363 {
364 endpoint.status = Some("online".into());
365 let _ = to_server_tx.send(Message::EndpointStatus(endpoint.clone())).await;
366 }
367 result_tx
368 .send(Message::EndpointAck(endpoint))
369 .await
370 .context("Can't send EndpointAck event")?;
371 }
372
373 Message::CreateDataChannelWithId(create_msg) => {
374 let channel_id = create_msg.channel_id;
375 let endpoint = create_msg.endpoint.unwrap();
376
377 trace!("Creating data channel {} for endpoint {:?}", channel_id, endpoint.guid);
378
379 let (to_service_tx, to_service_rx) = mpsc::channel::<Data>(DATA_CHANNEL_SIZE);
381
382 let data_channel = Arc::new(DataChannel::new_client(channel_id, to_service_tx.clone()));
384 self.data_channels.insert(channel_id, data_channel.clone());
385
386 let client = endpoint.client.unwrap();
388 #[allow(unused_mut)]
389 let mut local_addr = format!("{}:{}", client.local_addr, client.local_port);
390 #[cfg(feature = "plugins")]
391 if let Some(handle) = self.plugin_processes.get(&endpoint.guid) {
392 if let Some(port) = handle.value().port() {
393 local_addr = format!("127.0.0.1:{}", port);
394 }
395 }
396
397 let data_channels = self.data_channels.clone();
399 let protocol: Protocol = client.local_proto.try_into().unwrap();
400
401 let to_server_tx_cloned = to_server_tx.clone();
402 tokio::spawn(async move {
403 if let Err(err) = if protocol == Protocol::Udp {
404 handle_udp_data_channel(
405 data_channel,
406 local_addr,
407 to_server_tx_cloned.clone(),
408 to_service_rx
409 ).await
410 } else {
411 handle_tcp_data_channel(
412 data_channel,
413 local_addr,
414 to_server_tx_cloned.clone(),
415 to_service_rx
416 ).await
417 } {
418 error!("DataChannel {{ channel_id: {} }}: {:?}", channel_id, err);
419 to_server_tx_cloned
420 .send(Message::DataChannelEof(
421 DataChannelEof {
422 channel_id,
423 error: err.to_string()
424 })
425 ).await.ok();
426 }
427 if let Some((_, dc)) = data_channels.remove(&channel_id) { dc.close() }
428 });
429 },
430
431 Message::DataChannelData(data) => {
432 let to_service_tx = self.data_channels.get(&data.channel_id).map(|ch| ch.data_tx.clone());
434 if let Some(tx) = to_service_tx {
435 if let Err(err) = tx.send(Data {
436 data: data.data.into(),
437 socket_addr: None
438 }).await {
439 self.data_channels.remove(&data.channel_id);
440 error!("Error send to data channel {}: {:?}", data.channel_id, err);
441 }
442 } else {
443 trace!("Data channel {} not found, dropping data", data.channel_id);
444 }
445 },
446
447 Message::DataChannelDataUdp(data) => {
448 let to_service_tx = self.data_channels.get(&data.channel_id).map(|ch| ch.data_tx.clone());
450 if let Some(tx) = to_service_tx {
451 let socket_addr = data.socket_addr.as_ref()
452 .map(proto_to_socket_addr)
453 .transpose()
454 .unwrap_or_else(|err| {
455 error!("Invalid socket address for UDP data channel {}: {:?}", data.channel_id, err);
456 None
457 });
458
459 if let Err(err) = tx.send(Data {
460 data: data.data.into(),
461 socket_addr,
462 }).await {
463 self.data_channels.remove(&data.channel_id);
464 error!("Error send to UDP data channel {}: {:?}", data.channel_id, err);
465 }
466 } else {
467 trace!("UDP Data channel {} not found, dropping data", data.channel_id);
468 }
469 },
470
471 Message::DataChannelEof(eof) => {
472 if let Some((_, dc)) = self.data_channels.remove(&eof.channel_id) { dc.close() }
474 if eof.error.is_empty() {
475 trace!("Data channel {} closed by server", eof.channel_id);
477 } else {
478 trace!("Data channel {} closed by server with error: {}", eof.channel_id, eof.error);
480 }
481 },
482
483 Message::DataChannelAck(DataChannelAck { channel_id, consumed }) => {
484 if let Some(ch) = self.data_channels.get(&channel_id) {
485 ch.add_capacity(consumed);
486 }
487 }
488
489 Message::EndpointStopAck(ref ep) => {
490 self.services.remove(&ep.guid);
491 #[cfg(feature = "plugins")]
492 self.plugin_processes.remove(&ep.guid);
493 result_tx.send(val).await.context("Can't send result message")?;
494 }
495
496 Message::EndpointRemoveAck(ref ep) => {
497 self.services.remove(&ep.guid);
498 #[cfg(feature = "plugins")]
499 self.plugin_processes.remove(&ep.guid);
500 result_tx.send(val).await.context("Can't send result message")?;
501 }
502
503 Message::HeartBeat(_) => {
504 conn.send_message(&Message::HeartBeat(HeartBeat{})).await.context("Failed to send heartbeat")?;
505 },
506
507 Message::Error(ref err) => {
508 let kind: ErrorKind = err.kind.try_into().unwrap_or(ErrorKind::Fatal);
509 result_tx.send(val.clone()).await.context("Can't send result message")?;
510 if kind == ErrorKind::Fatal || kind == ErrorKind::AuthFailed {
511 error!("Fatal error received, stop client: {:?}", err);
512 break;
513 }
514 }
515
516 Message::Break(break_msg) => {
517 info!("Breaking operation for guid: {}", break_msg.guid);
518 #[cfg(feature = "plugins")]
519 self.plugin_processes.remove(&break_msg.guid);
520 }
521
522 Message::PerformUpgrade(info) => {
523 let config_clone = self.config.clone();
524 #[cfg(feature = "plugins")]
525 self.plugin_processes.clear();
526 self.services.clear();
527 self.data_channels.clear();
528
529 if let Err(e) = handle_upgrade_available(
530 &info.version,
531 config_clone,
532 self.opts.gui,
533 command_rx,
534 result_tx,
535 )
536 .await
537 {
538 conn.send_message(&Message::Error(ErrorInfo {
539 kind: ErrorKind::UpgradeFailed.into(),
540 message: e.to_string(),
541 guid: String::new(),
542 }))
543 .await
544 .context("Can't send Error event")?;
545 }
546 }
547
548 v => {
549 result_tx.send(v).await.context("Can't send result message")?;
550 }
551 }
552 },
553 None => {
554 debug!("Connection closed by server");
555 break;
556 }
557 }
558 },
559 _ = time::sleep(Duration::from_secs(heartbeat_timeout)), if heartbeat_timeout != 0 => {
560 return Err(anyhow!("Heartbeat timed out"))
561 }
562 }
563 }
564
565 info!("Control channel shutdown");
566 result_tx
567 .send(Message::ConnectState(ConnectState::Disconnected.into()))
568 .await
569 .context("Can't send Disconnected event")?;
570 conn.close().await.ok();
571 time::sleep(Duration::from_millis(100)).await; Ok(())
573 }
574}
575
576pub async fn run_client(
577 config: Arc<RwLock<ClientConfig>>,
578 opts: ClientOpts,
579 command_rx: mpsc::Receiver<Message>,
580 result_tx: mpsc::Sender<Message>,
581) -> Result<()> {
582 let mut client = Client::<WebsocketTransport>::from(config, opts)
583 .await
584 .context("Failed to create Websocket client")?;
585 client.run(command_rx, result_tx).await
586}
587async fn handle_tcp_data_channel(
588 data_channel: Arc<DataChannel>,
589 local_addr: String,
590 to_server_tx: FairSender<Message>,
591 mut data_rx: mpsc::Receiver<Data>,
592) -> Result<()> {
593 trace!("Handling client {:?} to {}", data_channel, local_addr);
594
595 let mut local_stream = TcpStream::connect(&local_addr)
597 .await
598 .with_context(|| format!("Failed to connect to local service at {}", local_addr))?;
599
600 local_stream
602 .set_nodelay(true)
603 .context("Failed to set TCP_NODELAY")?;
604
605 let mut buf = [0u8; DATA_BUFFER_SIZE]; loop {
608 tokio::select! {
609 res = local_stream.read(&mut buf) => {
610 match res {
611 Ok(0) => {
612 trace!("EOF received from local service for {:?}", data_channel);
613 if let Err(err) = to_server_tx.send(Message::DataChannelEof(DataChannelEof {
614 channel_id: data_channel.id,
615 error: String::new()
616 }))
617 .await {
618 trace!("Failed to send EOF to server for {:?}: {:#}", data_channel, err);
619 }
620 break;
621 },
622 Ok(n) => {
623 if data_channel.wait_for_capacity(n as u32).await.is_err() {
625 trace!("Data channel {} closed when waiting for capacity", data_channel.id);
626 break;
627 }
628 if let Err(err) = to_server_tx.send(Message::DataChannelData(DataChannelData {
629 channel_id: data_channel.id,
630 data: buf[0..n].to_vec()
631 }))
632 .await {
633 trace!("Failed to send data to server for {:?}: {:#}", data_channel, err);
634 break;
635 }
636 },
637 Err(e) => {
638 return Err(e).context("Failed to read from local service");
639 }
640 }
641 }
642
643 data_result = data_rx.recv() => {
645 match data_result {
646 Some(data) => {
647 trace!("Received {} bytes from server for {:?}", data.data.len(), data_channel);
648 local_stream.write_all(&data.data).await.context("Failed to write data to local service")?;
649 to_server_tx.send(Message::DataChannelAck(
650 DataChannelAck {
651 channel_id: data_channel.id,
652 consumed: data.data.len() as u32
653 }
654 )).await.with_context(|| "Failed to send TCP traffic ack to the server")?;
655 },
656 None => {
657 trace!("EOF received from server for {:?}", data_channel);
658 break;
659 }
660 }
661 }
662
663 _ = data_channel.closed() => {
664 trace!("Data channel {} closed", data_channel.id);
665 break;
666 }
667 }
668 }
669 Ok(())
670}
671
672type UdpPortMap = Arc<DashMap<SocketAddr, mpsc::Sender<Bytes>>>;
674
675async fn handle_udp_data_channel(
676 data_channel: Arc<DataChannel>,
677 local_addr: String,
678 to_server_tx: FairSender<Message>,
679 mut data_rx: mpsc::Receiver<Data>,
680) -> Result<()> {
681 trace!(
682 "Handling client UDP channel {:?} to {}",
683 data_channel,
684 local_addr
685 );
686
687 let port_map: UdpPortMap = Arc::new(DashMap::new());
688
689 loop {
690 let data_channel = data_channel.clone();
691 tokio::select! {
693 data = data_rx.recv() => {
694 match data {
695 Some(data) => {
696 let external_addr = data.socket_addr.unwrap();
697
698 if !port_map.contains_key(&external_addr) {
699 match udp_connect(&local_addr).await {
704 Ok(s) => {
705 let (to_service_tx, to_service_rx) = mpsc::channel(DATA_CHANNEL_SIZE);
706 port_map.insert(external_addr, to_service_tx);
707 tokio::spawn(run_udp_forwarder(
708 s,
709 to_service_rx,
710 to_server_tx.clone(),
711 external_addr,
712 data_channel,
713 port_map.clone(),
714 ));
715 }
716 Err(e) => {
717 error!(
718 "Failed to create UDP forwarder for {}: {:#}",
719 external_addr, e
720 );
721 }
722 }
723 }
724
725 if let Some(tx) = port_map.get(&external_addr) {
727 let _ = tx.send(data.data).await;
728 }
729 }
730 None => {
731 trace!("EOF received from server for UDP {:?}", data_channel);
732 break;
733 }
734 }
735 }
736 _ = data_channel.closed() => {
737 trace!("Data channel {} closed", data_channel.id);
738 break;
739 }
740 }
741 }
742 Ok(())
743}
744
745async fn run_udp_forwarder(
747 s: UdpSocket,
748 mut to_service_rx: mpsc::Receiver<Bytes>,
749 to_server_tx: FairSender<Message>,
750 from: SocketAddr,
751 data_channel: Arc<DataChannel>,
752 port_map: UdpPortMap,
753) -> Result<()> {
754 trace!("UDP forwarder created for {} on {:?}", from, data_channel);
755 let mut buf = vec![0u8; UDP_BUFFER_SIZE];
756
757 loop {
758 tokio::select! {
759 data = to_service_rx.recv() => {
761 if let Some(data) = data {
762 s.send(&data).await.with_context(|| "Failed to send UDP traffic to the service")?;
763 to_server_tx.send(Message::DataChannelAck(
764 DataChannelAck {
765 channel_id: data_channel.id,
766 consumed: data.len() as u32
767 }
768 )).await.with_context(|| "Failed to send UDP traffic ack to the server")?;
769 } else {
770 break;
771 }
772 },
773
774 val = s.recv(&mut buf) => {
776 let len = match val {
777 Ok(v) => v,
778 Err(_) => break
779 };
780
781 if data_channel.wait_for_capacity(len as u32).await.is_err() {
782 break;
783 }
784
785 to_server_tx.send(Message::DataChannelDataUdp(
786 DataChannelDataUdp {
787 channel_id: data_channel.id,
788 data: buf[..len].to_vec(),
789 socket_addr: Some(socket_addr_to_proto(&from)),
790 })).await.with_context(|| "Failed to send UDP traffic to the server")?;
791 },
792
793 _ = time::sleep(Duration::from_secs(UDP_TIMEOUT)) => {
795 break;
796 }
797
798 _ = data_channel.closed() => {
799 trace!("Data channel {} closed", data_channel.id);
800 break;
801 }
802 }
803 }
804
805 port_map.remove(&from);
806
807 debug!("UDP forwarder dropped for {} on {:?}", from, data_channel);
808 Ok(())
809}