escvpnet/
client.rs

1use std::{net::SocketAddr, pin::Pin, time::Duration};
2
3use crate::{
4    header::{Header, HeaderIdentifier},
5    io::{DecodeFrom, EncodeTo},
6    packet::{Packet, PacketCategory, Status},
7    Result,
8};
9use tokio::{
10    io::AsyncWriteExt,
11    io::{BufReader, BufWriter},
12    net::{TcpStream, ToSocketAddrs, UdpSocket},
13};
14
15const HELLO_PACKET: [u8; 16] = [
16    b'E', b'S', b'C', b'/', b'V', b'P', b'.', b'n', b'e', b't', // Protocol Header
17    0x10, // Protocol version
18    1,    // Type identifier
19    0, 0, // Reserved
20    0, //Status code
21    0, // Number of headers
22];
23const BUF_SIZE: usize = 1024;
24
25const CONNECT_PACKET: Packet = Packet {
26    category: PacketCategory::Connect,
27    status: Status::Null,
28    headers: vec![],
29};
30
31pub struct Client {
32    stream: TcpStream,
33}
34
35impl Client {
36    pub async fn discover<A: ToSocketAddrs>(
37        bind_addr: A,
38        broadcast_addr: A,
39        timeout: Duration,
40    ) -> Result<Vec<Projector>> {
41        let socket = UdpSocket::bind(bind_addr).await?;
42
43        socket.set_broadcast(true)?;
44
45        socket.send_to(&HELLO_PACKET, broadcast_addr).await?;
46        let mut projectors = Vec::new();
47        let mut buf = [0; BUF_SIZE];
48
49        while let Ok((n, addr)) =
50            match tokio::time::timeout(timeout, socket.recv_from(&mut buf)).await {
51                Ok(result) => result,
52                Err(_) => return Ok(projectors),
53            }
54        {
55            let packet = Packet::decode_from(&mut Pin::new(&mut &buf[..n])).await?; // handle result
56            let name = packet
57                .headers
58                .iter()
59                .find(|h| matches!(h.identifier(), HeaderIdentifier::ProjectorName))
60                .map(|h| h.information().to_string());
61            projectors.push(Projector { addr, name })
62        }
63        Ok(projectors)
64    }
65
66    pub async fn connect<A: ToSocketAddrs>(
67        addr: A,
68        password: Option<String>,
69        timeout: Duration,
70    ) -> Result<Self> {
71        let mut stream = tokio::time::timeout(timeout, TcpStream::connect(addr))
72            .await
73            .map_err(|_| {
74                crate::Error::new(
75                    crate::error::ErrorKind::IO(std::io::ErrorKind::TimedOut),
76                    "Timed out".to_string(),
77                )
78            })??;
79        {
80            let (reader, writer) = stream.split();
81            let mut writer = BufWriter::new(writer);
82            let mut pinned_writer = Pin::new(&mut writer);
83            let mut reader = BufReader::new(reader);
84            let mut pinned_reader = Pin::new(&mut reader);
85
86            let packet = match password {
87                None => CONNECT_PACKET,
88                Some(password) => {
89                    let mut packet = CONNECT_PACKET;
90                    packet
91                        .headers
92                        .push(Header::new(HeaderIdentifier::Password, 1, password)?);
93                    packet
94                }
95            };
96
97            packet.encode_to(&mut pinned_writer).await?;
98            writer.flush().await?;
99            Packet::decode_from(&mut pinned_reader)
100                .await?
101                .status_as_result()?;
102        }
103        Ok(Self { stream })
104    }
105
106    pub async fn send_packet(&mut self, packet: Packet) -> Result<Packet> {
107        let (reader, writer) = self.stream.split();
108        let mut writer = BufWriter::new(writer);
109        let mut pinned_writer = Pin::new(&mut writer);
110        let mut reader = BufReader::new(reader);
111        let mut pinned_reader = Pin::new(&mut reader);
112        packet.encode_to(&mut pinned_writer).await?;
113
114        Packet::decode_from(&mut pinned_reader).await
115    }
116}
117
118pub struct Projector {
119    addr: SocketAddr,
120    name: Option<String>,
121}
122
123impl Projector {
124    pub fn addr(&self) -> SocketAddr {
125        self.addr
126    }
127    pub fn name(&self) -> Option<String> {
128        self.name.clone()
129    }
130}