Skip to main content

moblink_rust/
relay.rs

1use std::future::Future;
2use std::net::{IpAddr, SocketAddr};
3use std::pin::Pin;
4use std::str::FromStr;
5use std::sync::{Arc, Weak};
6
7use futures_util::stream::{SplitSink, SplitStream};
8use futures_util::{SinkExt, StreamExt};
9use log::{debug, error, info};
10use serde::Deserialize;
11use tokio::fs::File;
12use tokio::io::AsyncReadExt;
13use tokio::net::{TcpStream, UdpSocket};
14use tokio::process::Command;
15use tokio::sync::Mutex;
16use tokio::time::{Duration, sleep, timeout};
17use tokio_tungstenite::tungstenite::protocol::Message;
18use tokio_tungstenite::{MaybeTlsStream, WebSocketStream, connect_async};
19use uuid::Uuid;
20
21use crate::protocol::*;
22use crate::utils::{AnyError, resolve_host};
23
24#[derive(Default, Deserialize, Clone)]
25#[serde(rename_all = "camelCase")]
26pub struct Status {
27    pub battery_percentage: Option<i32>,
28}
29
30pub type GetStatusClosure =
31    Box<dyn Fn() -> Pin<Box<dyn Future<Output = Status> + Send + Sync>> + Send + Sync>;
32
33struct RelayInner {
34    me: Weak<Mutex<Self>>,
35    /// Store a local IP address  for binding UDP sockets
36    bind_address: String,
37    relay_id: Uuid,
38    streamer_url: String,
39    password: String,
40    name: String,
41    on_status_updated: Option<Box<dyn Fn(String) + Send + Sync>>,
42    get_status: Option<Arc<GetStatusClosure>>,
43    ws_writer: Option<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>>,
44    started: bool,
45    connected: bool,
46    wrong_password: bool,
47    reconnect_on_tunnel_error: Arc<Mutex<bool>>,
48    start_on_reconnect_soon: Arc<Mutex<bool>>,
49    relay_to_destination: Option<tokio::task::JoinHandle<Result<(), AnyError>>>,
50    relay_to_streamer: Arc<Mutex<Option<tokio::task::AbortHandle>>>,
51}
52
53impl RelayInner {
54    fn new() -> Arc<Mutex<Self>> {
55        Arc::new_cyclic(|me| {
56            Mutex::new(Self {
57                me: me.clone(),
58                bind_address: Self::get_default_bind_address(),
59                relay_id: Uuid::new_v4(),
60                streamer_url: "".to_string(),
61                password: "".to_string(),
62                name: "".to_string(),
63                on_status_updated: None,
64                get_status: None,
65                ws_writer: None,
66                started: false,
67                connected: false,
68                wrong_password: false,
69                reconnect_on_tunnel_error: Arc::new(Mutex::new(false)),
70                start_on_reconnect_soon: Arc::new(Mutex::new(false)),
71                relay_to_destination: None,
72                relay_to_streamer: Arc::new(Mutex::new(None)),
73            })
74        })
75    }
76
77    fn set_bind_address(&mut self, address: String) {
78        self.bind_address = address;
79    }
80
81    async fn setup<F>(
82        &mut self,
83        streamer_url: String,
84        password: String,
85        relay_id: Uuid,
86        name: String,
87        on_status_updated: F,
88        get_status: Option<GetStatusClosure>,
89    ) where
90        F: Fn(String) + Send + Sync + 'static,
91    {
92        self.on_status_updated = Some(Box::new(on_status_updated));
93        self.get_status = get_status.map(Arc::new);
94        self.relay_id = relay_id;
95        self.streamer_url = streamer_url;
96        self.password = password;
97        self.name = name;
98    }
99
100    fn is_started(&self) -> bool {
101        self.started
102    }
103
104    async fn start(&mut self) {
105        if !self.started {
106            self.started = true;
107            self.start_internal().await;
108        }
109    }
110
111    async fn stop(&mut self) {
112        if self.started {
113            self.started = false;
114            self.stop_internal().await;
115        }
116    }
117
118    fn get_default_bind_address() -> String {
119        // Get main network interface
120        let interfaces = pnet::datalink::interfaces();
121        let interface = interfaces.iter().find(|interface| {
122            interface.is_up() && !interface.is_loopback() && !interface.ips.is_empty()
123        });
124
125        // Only ipv4 addresses are supported
126        let ipv4_addresses: Vec<String> = interface
127            .expect("No available network interfaces found")
128            .ips
129            .iter()
130            .filter_map(|ip| {
131                let ip = ip.ip();
132                ip.is_ipv4().then(|| ip.to_string())
133            })
134            .collect();
135
136        // Return the first address
137        ipv4_addresses
138            .first()
139            .cloned()
140            .unwrap_or("0.0.0.0:0".to_string())
141    }
142
143    async fn start_internal(&mut self) {
144        if !self.started {
145            self.stop_internal().await;
146            return;
147        }
148
149        let request = match url::Url::parse(&self.streamer_url) {
150            Ok(url) => url,
151            Err(e) => {
152                error!("Failed to parse URL: {}", e);
153                return;
154            }
155        };
156
157        match timeout(Duration::from_secs(10), connect_async(request.to_string())).await {
158            Ok(Ok((ws_stream, _))) => {
159                debug!("Connected to {}", self.streamer_url);
160                let (writer, reader) = ws_stream.split();
161                self.ws_writer = Some(writer);
162                self.start_websocket_receiver(reader);
163            }
164            Ok(Err(error)) => {
165                debug!(
166                    "Failed to connect to {} with error: {}",
167                    self.streamer_url, error
168                );
169                self.reconnect_soon().await;
170            }
171            Err(_elapsed) => {
172                debug!(
173                    "Failed to connect to {} within 10 seconds",
174                    self.streamer_url
175                );
176                self.reconnect_soon().await;
177            }
178        }
179    }
180
181    fn start_websocket_receiver(
182        &mut self,
183        mut reader: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
184    ) {
185        // Task to process messages received from the channel.
186        let relay = self.me.clone();
187
188        tokio::spawn(async move {
189            let Some(relay_arc) = relay.upgrade() else {
190                return;
191            };
192
193            while let Some(result) = reader.next().await {
194                let mut relay = relay_arc.lock().await;
195                match result {
196                    Ok(message) => match message {
197                        Message::Text(text) => {
198                            match serde_json::from_str::<MessageToRelay>(&text) {
199                                Ok(message) => {
200                                    if let Err(error) = relay.handle_message(message).await {
201                                        error!("Message handling failed with error: {}", error);
202                                        relay.reconnect_soon().await;
203                                        break;
204                                    }
205                                }
206                                _ => {
207                                    error!("Failed to deserialize message: {}", text);
208                                }
209                            }
210                        }
211                        Message::Binary(data) => {
212                            debug!("Received binary message of length: {}", data.len());
213                        }
214                        Message::Ping(data) => {
215                            relay.send_message(Message::Pong(data)).await.ok();
216                        }
217                        Message::Pong(_) => {
218                            debug!("Received pong message");
219                        }
220                        Message::Close(frame) => {
221                            info!("Received close message: {:?}", frame);
222                            relay.reconnect_soon().await;
223                            break;
224                        }
225                        Message::Frame(_) => {
226                            unreachable!("This is never used")
227                        }
228                    },
229                    Err(e) => {
230                        debug!("Error processing message: {}", e);
231                        // TODO: There has to be a better way to handle this
232                        if e.to_string()
233                            .contains("Connection reset without closing handshake")
234                        {
235                            relay.reconnect_soon().await;
236                        }
237                        break;
238                    }
239                }
240            }
241        });
242    }
243
244    async fn stop_internal(&mut self) {
245        if let Some(mut ws_writer) = self.ws_writer.take() {
246            match ws_writer.close().await {
247                Err(e) => {
248                    error!("Error closing WebSocket: {}", e);
249                }
250                _ => {
251                    debug!("WebSocket closed successfully");
252                }
253            }
254        }
255        self.connected = false;
256        self.wrong_password = false;
257        *self.start_on_reconnect_soon.lock().await = false;
258        self.stop_tunnel().await;
259        self.update_status();
260    }
261
262    /// The streamer asks for a new tunnel on every reconnect attempt, reusing
263    /// the same websocket. Without tearing the old one down first, its
264    /// tasks stay parked on sockets nobody sends to for the rest of the
265    /// session.
266    async fn stop_tunnel(&mut self) {
267        *self.reconnect_on_tunnel_error.lock().await = false;
268        if let Some(relay_to_destination) = self.relay_to_destination.take() {
269            relay_to_destination.abort();
270            relay_to_destination.await.ok();
271        }
272        // Taken after joining above, as that task is what fills the slot in.
273        if let Some(relay_to_streamer) = self.relay_to_streamer.lock().await.take() {
274            relay_to_streamer.abort();
275        }
276    }
277
278    fn update_status(&self) {
279        let Some(on_status_updated) = &self.on_status_updated else {
280            return;
281        };
282        let status = if self.connected {
283            "Connected to streamer"
284        } else if self.wrong_password {
285            "Wrong password"
286        } else if self.started {
287            "Connecting to streamer"
288        } else {
289            "Disconnected from streamer"
290        };
291        on_status_updated(status.to_string());
292    }
293
294    async fn reconnect_soon(&mut self) {
295        self.stop_internal().await;
296        *self.start_on_reconnect_soon.lock().await = false;
297        let start_on_reconnect_soon = Arc::new(Mutex::new(true));
298        self.start_on_reconnect_soon = start_on_reconnect_soon.clone();
299        self.start_soon(start_on_reconnect_soon);
300    }
301
302    fn start_soon(&mut self, start_on_reconnect_soon: Arc<Mutex<bool>>) {
303        let relay = self.me.clone();
304
305        tokio::spawn(async move {
306            sleep(Duration::from_secs(5)).await;
307
308            if *start_on_reconnect_soon.lock().await {
309                debug!("Reconnecting...");
310                if let Some(relay) = relay.upgrade() {
311                    relay.lock().await.start_internal().await;
312                }
313            }
314        });
315    }
316
317    async fn handle_message(&mut self, message: MessageToRelay) -> Result<(), AnyError> {
318        match message {
319            MessageToRelay::Hello(hello) => self.handle_message_hello(hello).await,
320            MessageToRelay::Identified(identified) => {
321                self.handle_message_identified(identified).await
322            }
323            MessageToRelay::Request(request) => self.handle_message_request(request).await,
324        }
325    }
326
327    async fn handle_message_hello(&mut self, hello: Hello) -> Result<(), AnyError> {
328        let authentication = calculate_authentication(
329            &self.password,
330            &hello.authentication.salt,
331            &hello.authentication.challenge,
332        );
333        let identify = Identify {
334            id: self.relay_id,
335            name: self.name.clone(),
336            authentication,
337        };
338        self.send(MessageToStreamer::Identify(identify)).await
339    }
340
341    async fn handle_message_identified(&mut self, identified: Identified) -> Result<(), AnyError> {
342        match identified.result {
343            MoblinkResult::Ok(_) => {
344                self.connected = true;
345            }
346            MoblinkResult::WrongPassword(_) => {
347                self.wrong_password = true;
348            }
349        }
350        self.update_status();
351        Ok(())
352    }
353
354    async fn handle_message_request(&mut self, request: MessageRequest) -> Result<(), AnyError> {
355        match &request.data {
356            MessageRequestData::StartTunnel(start_tunnel) => {
357                self.handle_message_request_start_tunnel(&request, start_tunnel)
358                    .await
359            }
360            MessageRequestData::Status(_) => self.handle_message_request_status(request).await,
361        }
362    }
363
364    async fn handle_message_request_start_tunnel(
365        &mut self,
366        request: &MessageRequest,
367        start_tunnel: &StartTunnelRequest,
368    ) -> Result<(), AnyError> {
369        self.stop_tunnel().await;
370
371        // Pick bind addresses from the relay
372        let local_bind_addr_for_streamer = parse_socket_addr("0.0.0.0")?;
373        let local_bind_addr_for_destination = parse_socket_addr(&self.bind_address)?;
374
375        debug!(
376            "Binding streamer socket on: {}, destination socket on: {}",
377            local_bind_addr_for_streamer, local_bind_addr_for_destination
378        );
379        // Create a UDP socket bound for receiving packets from the server.
380        // Use dual-stack socket creation.
381        let streamer_socket = create_dual_stack_udp_socket(local_bind_addr_for_streamer).await?;
382        let streamer_port = streamer_socket.local_addr()?.port();
383        let streamer_socket = Arc::new(streamer_socket);
384
385        // Inform the server about the chosen port.
386        let data = ResponseData::StartTunnel(StartTunnelResponseData {
387            port: streamer_port,
388        });
389        let response = request.to_ok_response(data);
390        self.send(MessageToStreamer::Response(response)).await?;
391
392        // Create a new UDP socket for communication with the destination.
393        // Use dual-stack socket creation.
394        let destination_socket =
395            create_dual_stack_udp_socket(local_bind_addr_for_destination).await?;
396
397        let destination_socket = Arc::new(destination_socket);
398        let destination_address = resolve_host(&start_tunnel.address).await?;
399        let destination_address = match IpAddr::from_str(&destination_address)? {
400            IpAddr::V4(v4) => IpAddr::V4(v4),
401            IpAddr::V6(v6) => {
402                // If it’s an IPv4-mapped IPv6 like ::ffff:x.x.x.x, convert to real IPv4
403                if let Some(mapped_v4) = v6.to_ipv4() {
404                    IpAddr::V4(mapped_v4)
405                } else {
406                    // Otherwise, keep it as IPv6
407                    IpAddr::V6(v6)
408                }
409            }
410        };
411
412        let destination_address = SocketAddr::new(destination_address, start_tunnel.port);
413        info!("Destination address: {}", destination_address);
414
415        self.relay_to_destination = Some(
416            self.start_relay_from_streamer_to_destination(
417                streamer_socket,
418                destination_socket,
419                destination_address,
420            )
421            .await,
422        );
423
424        Ok(())
425    }
426
427    async fn start_relay_from_streamer_to_destination(
428        &mut self,
429        streamer_socket: Arc<UdpSocket>,
430        destination_socket: Arc<UdpSocket>,
431        destination_addr: SocketAddr,
432    ) -> tokio::task::JoinHandle<Result<(), AnyError>> {
433        let reconnect_on_tunnel_error = Arc::new(Mutex::new(true));
434        self.reconnect_on_tunnel_error = reconnect_on_tunnel_error.clone();
435        let relay_to_streamer = Arc::new(Mutex::new(None));
436        self.relay_to_streamer = relay_to_streamer.clone();
437        let relay = self.me.clone();
438
439        tokio::spawn(async move {
440            let streamer_address = Arc::new(Mutex::new(None));
441            let mut relay_to_streamer_started = false;
442            let mut buf = [0; 2048];
443
444            loop {
445                let (size, remote_addr) = streamer_socket.recv_from(&mut buf).await?;
446                destination_socket
447                    .send_to(&buf[..size], &destination_addr)
448                    .await?;
449                streamer_address.lock().await.replace(remote_addr);
450
451                if !relay_to_streamer_started {
452                    let task = start_relay_from_destination_to_streamer(
453                        relay.clone(),
454                        streamer_socket.clone(),
455                        destination_socket.clone(),
456                        streamer_address.clone(),
457                        reconnect_on_tunnel_error.clone(),
458                    );
459                    relay_to_streamer.lock().await.replace(task);
460                    relay_to_streamer_started = true;
461                }
462            }
463        })
464    }
465
466    async fn handle_message_request_status(
467        &mut self,
468        request: MessageRequest,
469    ) -> Result<(), AnyError> {
470        let mut battery_percentage = None;
471        if let Some(get_status) = self.get_status.as_ref() {
472            battery_percentage = get_status().await.battery_percentage;
473        }
474        let data = ResponseData::Status(StatusResponseData { battery_percentage });
475        let response = request.to_ok_response(data);
476        self.send(MessageToStreamer::Response(response)).await
477    }
478
479    async fn send(&mut self, message: MessageToStreamer) -> Result<(), AnyError> {
480        let text = serde_json::to_string(&message)?;
481        self.send_message(Message::Text(text.into())).await
482    }
483
484    async fn send_message(&mut self, message: Message) -> Result<(), AnyError> {
485        let Some(writer) = self.ws_writer.as_mut() else {
486            return Err("No websocket writer".into());
487        };
488        writer.send(message).await?;
489        Ok(())
490    }
491}
492
493pub struct Relay {
494    inner: Arc<Mutex<RelayInner>>,
495}
496
497impl Default for Relay {
498    fn default() -> Self {
499        Self::new()
500    }
501}
502
503impl Relay {
504    pub fn new() -> Self {
505        Self {
506            inner: RelayInner::new(),
507        }
508    }
509
510    pub async fn set_bind_address(&self, address: String) {
511        self.inner.lock().await.set_bind_address(address);
512    }
513
514    pub async fn setup<F>(
515        &self,
516        streamer_url: String,
517        password: String,
518        relay_id: Uuid,
519        name: String,
520        on_status_updated: F,
521        get_status: Option<GetStatusClosure>,
522    ) where
523        F: Fn(String) + Send + Sync + 'static,
524    {
525        self.inner
526            .lock()
527            .await
528            .setup(
529                streamer_url,
530                password,
531                relay_id,
532                name,
533                on_status_updated,
534                get_status,
535            )
536            .await;
537    }
538
539    pub async fn is_started(&self) -> bool {
540        self.inner.lock().await.is_started()
541    }
542
543    pub async fn start(&self) {
544        self.inner.lock().await.start().await;
545    }
546
547    pub async fn stop(&self) {
548        self.inner.lock().await.stop().await;
549    }
550}
551
552fn start_relay_from_destination_to_streamer(
553    relay: Weak<Mutex<RelayInner>>,
554    streamer_socket: Arc<UdpSocket>,
555    destination_socket: Arc<UdpSocket>,
556    streamer_address: Arc<Mutex<Option<SocketAddr>>>,
557    reconnect_on_tunnel_error: Arc<Mutex<bool>>,
558) -> tokio::task::AbortHandle {
559    tokio::spawn(async move {
560        loop {
561            if let Err(error) = relay_one_packet_from_destination_to_streamer(
562                &streamer_socket,
563                &destination_socket,
564                &streamer_address,
565            )
566            .await
567            {
568                info!("(relay_to_streamer) Failed with error: {}", error);
569                break;
570            }
571        }
572
573        if *reconnect_on_tunnel_error.lock().await {
574            if let Some(relay) = relay.upgrade() {
575                // From another task, as reconnecting tears down the tunnel, which
576                // aborts this one before it would get around to reconnecting.
577                tokio::spawn(async move {
578                    relay.lock().await.reconnect_soon().await;
579                });
580            }
581        } else {
582            info!("Not reconnecting after tunnel error");
583        }
584    })
585    .abort_handle()
586}
587
588async fn relay_one_packet_from_destination_to_streamer(
589    streamer_socket: &Arc<UdpSocket>,
590    destination_socket: &Arc<UdpSocket>,
591    streamer_address: &Arc<Mutex<Option<SocketAddr>>>,
592) -> Result<(), AnyError> {
593    let mut buf = [0; 2048];
594    let size = timeout(Duration::from_secs(30), destination_socket.recv(&mut buf)).await??;
595    let streamer_addr = streamer_address
596        .lock()
597        .await
598        .ok_or("Failed to get address lock")?;
599    streamer_socket
600        .send_to(&buf[..size], &streamer_addr)
601        .await?;
602    Ok(())
603}
604
605async fn create_dual_stack_udp_socket(
606    addr: SocketAddr,
607) -> Result<tokio::net::UdpSocket, std::io::Error> {
608    let socket = match addr.is_ipv4() {
609        true => {
610            // Create an IPv4 socket
611            tokio::net::UdpSocket::bind(addr).await?
612        }
613        false => {
614            // Create a dual-stack socket (supporting both IPv4 and IPv6)
615            let socket = socket2::Socket::new(
616                socket2::Domain::IPV6,
617                socket2::Type::DGRAM,
618                Some(socket2::Protocol::UDP),
619            )?;
620
621            // Set IPV6_V6ONLY to false to enable dual-stack support
622            socket.set_only_v6(false)?;
623
624            // Bind the socket
625            socket.bind(&socket2::SockAddr::from(addr))?;
626
627            // Convert to a tokio UdpSocket
628            tokio::net::UdpSocket::from_std(socket.into())?
629        }
630    };
631
632    Ok(socket)
633}
634
635// Helper function to parse a string into a SocketAddr, handling IP addresses
636// without ports.
637fn parse_socket_addr(addr_str: &str) -> Result<SocketAddr, std::io::Error> {
638    // Attempt to parse the string as a full SocketAddr (IP:port)
639    if let Ok(socket_addr) = SocketAddr::from_str(addr_str) {
640        return Ok(socket_addr);
641    }
642
643    // If parsing as SocketAddr fails, try parsing as IP address and append default
644    // port
645    if let Ok(ip_addr) = IpAddr::from_str(addr_str) {
646        // Use 0 as the default port, allowing the OS to assign an available port
647        return Ok(SocketAddr::new(ip_addr, 0));
648    }
649
650    // Return an error if both attempts fail
651    Err(std::io::Error::new(
652        std::io::ErrorKind::InvalidInput,
653        "Invalid socket address syntax. Expected 'IP:port' or 'IP'.",
654    ))
655}
656
657pub fn create_get_status_closure(
658    status_executable: &Option<String>,
659    status_file: &Option<String>,
660) -> Option<GetStatusClosure> {
661    let status_executable = status_executable.clone();
662    let status_file = status_file.clone();
663    Some(Box::new(move || {
664        let status_executable = status_executable.clone();
665        let status_file = status_file.clone();
666        Box::pin(async move {
667            let output = if let Some(status_executable) = &status_executable {
668                let Ok(output) = Command::new(status_executable).output().await else {
669                    return Default::default();
670                };
671                output.stdout
672            } else if let Some(status_file) = &status_file {
673                let Ok(mut file) = File::open(status_file).await else {
674                    return Default::default();
675                };
676                let mut contents = vec![];
677                if file.read_to_end(&mut contents).await.is_err() {
678                    return Default::default();
679                }
680                contents
681            } else {
682                return Default::default();
683            };
684            let output = String::from_utf8(output).unwrap_or_default();
685            match serde_json::from_str(&output) {
686                Ok(status) => status,
687                Err(e) => {
688                    error!("Failed to decode status with error: {e}");
689                    Default::default()
690                }
691            }
692        })
693    }))
694}