Skip to main content

cloudpub_common/
protocol.rs

1// Create a module to contain the Protocol Buffers generated code
2use anyhow::{anyhow, bail, Context, Result};
3use bytes::{Bytes, BytesMut};
4use serde::{Deserialize, Serialize};
5use std::fmt::{self, Display, Formatter};
6use std::str::FromStr;
7use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
8use tracing::{debug, trace};
9use urlencoding::encode;
10
11use crate::fair_channel::FairGroup;
12use crate::utils::get_version_number;
13pub use prost::Message as ProstMessage;
14
15pub trait Endpoint {
16    fn credentials(&self) -> String;
17    fn as_url(&self) -> String;
18}
19
20pub trait DefaultPort {
21    fn default_port(&self) -> Option<u16>;
22}
23
24pub fn parse_enum<E: FromStr + Into<i32>>(name: &str) -> Result<i32> {
25    let proto = E::from_str(name).map_err(|_| anyhow!("Invalid enum: {}", name))?;
26    Ok(proto.into())
27}
28
29pub fn str_enum<E: TryFrom<i32> + ToString>(e: i32) -> String {
30    e.try_into()
31        .map(|e: E| e.to_string())
32        .unwrap_or("unknown".to_string())
33}
34
35include!(concat!(env!("OUT_DIR"), "/protocol.rs"));
36
37impl ErrorKind {
38    /// Whether an error of this kind ends the client session. Everything else
39    /// is reported and survived - LeaseBusy in particular is temporary by
40    /// construction (the holder is evicted on the next attempt), so a consumer
41    /// may simply retry (CLOUDPUB-1339).
42    pub fn is_fatal(self) -> bool {
43        match self {
44            ErrorKind::Fatal | ErrorKind::AuthFailed => true,
45            ErrorKind::HandshakeFailed
46            | ErrorKind::PermissionDenied
47            | ErrorKind::PublishFailed
48            | ErrorKind::ExecuteFailed
49            | ErrorKind::ServerError
50            | ErrorKind::UpgradeFailed
51            | ErrorKind::LeaseBusy => false,
52        }
53    }
54}
55
56pub struct Data {
57    pub data: Bytes,
58    pub socket_addr: Option<std::net::SocketAddr>,
59}
60
61impl Display for Protocol {
62    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63        match self {
64            Protocol::Http => write!(f, "http"),
65            Protocol::Https => write!(f, "https"),
66            Protocol::Tcp => write!(f, "tcp"),
67            Protocol::Udp => write!(f, "udp"),
68            Protocol::OneC => write!(f, "1c"),
69            Protocol::Minecraft => write!(f, "minecraft"),
70            Protocol::Webdav => write!(f, "webdav"),
71            Protocol::Rtsp => write!(f, "rtsp"),
72            Protocol::Rdp => write!(f, "rdp"),
73            Protocol::Vnc => write!(f, "vnc"),
74            Protocol::Ssh => write!(f, "ssh"),
75        }
76    }
77}
78
79impl FromStr for Protocol {
80    type Err = anyhow::Error;
81
82    fn from_str(s: &str) -> Result<Self> {
83        match s {
84            "http" => Ok(Protocol::Http),
85            "https" => Ok(Protocol::Https),
86            "tcp" => Ok(Protocol::Tcp),
87            "udp" => Ok(Protocol::Udp),
88            "1c" => Ok(Protocol::OneC),
89            "minecraft" => Ok(Protocol::Minecraft),
90            "webdav" => Ok(Protocol::Webdav),
91            "rtsp" => Ok(Protocol::Rtsp),
92            "rdp" => Ok(Protocol::Rdp),
93            "vnc" => Ok(Protocol::Vnc),
94            "ssh" => Ok(Protocol::Ssh),
95            _ => bail!("Invalid protocol: {}", s),
96        }
97    }
98}
99
100impl DefaultPort for Protocol {
101    fn default_port(&self) -> Option<u16> {
102        match self {
103            Protocol::Http => Some(80),
104            Protocol::Https => Some(443),
105            Protocol::Tcp => None,
106            Protocol::Udp => None,
107            Protocol::OneC => None,
108            Protocol::Minecraft => Some(25565),
109            Protocol::Webdav => None,
110            Protocol::Rtsp => Some(554),
111            Protocol::Rdp => Some(3389),
112            Protocol::Vnc => Some(5900),
113            Protocol::Ssh => Some(22),
114        }
115    }
116}
117
118impl FromStr for Role {
119    type Err = anyhow::Error;
120
121    fn from_str(s: &str) -> Result<Self> {
122        match s {
123            "none" => Ok(Role::Nobody),
124            "admin" => Ok(Role::Admin),
125            "reader" => Ok(Role::Reader),
126            "writer" => Ok(Role::Writer),
127            _ => bail!("Invalid access: {}", s),
128        }
129    }
130}
131
132impl Display for Role {
133    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
134        match self {
135            Role::Nobody => write!(f, "none"),
136            Role::Admin => write!(f, "admin"),
137            Role::Reader => write!(f, "reader"),
138            Role::Writer => write!(f, "writer"),
139        }
140    }
141}
142
143impl FromStr for Auth {
144    type Err = anyhow::Error;
145
146    fn from_str(s: &str) -> Result<Self> {
147        match s {
148            "none" => Ok(Auth::None),
149            "basic" => Ok(Auth::Basic),
150            "form" => Ok(Auth::Form),
151            _ => bail!("Invalid auth: {}", s),
152        }
153    }
154}
155
156impl Display for Auth {
157    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
158        match self {
159            Auth::None => write!(f, "none"),
160            Auth::Basic => write!(f, "basic"),
161            Auth::Form => write!(f, "form"),
162        }
163    }
164}
165
166impl FromStr for ProxyProtocol {
167    type Err = anyhow::Error;
168
169    fn from_str(s: &str) -> Result<Self> {
170        match s {
171            "none" => Ok(ProxyProtocol::None),
172            "v2" => Ok(ProxyProtocol::V2),
173            _ => bail!("Invalid proxy protocol: {}", s),
174        }
175    }
176}
177
178impl Display for ProxyProtocol {
179    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
180        match self {
181            ProxyProtocol::None => write!(f, "none"),
182            ProxyProtocol::V2 => write!(f, "v2"),
183        }
184    }
185}
186
187impl FromStr for FilterAction {
188    type Err = anyhow::Error;
189
190    fn from_str(s: &str) -> Result<Self> {
191        match s {
192            "allow" => Ok(FilterAction::FilterAllow),
193            "deny" => Ok(FilterAction::FilterDeny),
194            "redirect" => Ok(FilterAction::FilterRedirect),
195            "log" => Ok(FilterAction::FilterLog),
196            _ => bail!("Invalid filter action: {}", s),
197        }
198    }
199}
200
201impl PartialEq for ClientEndpoint {
202    fn eq(&self, other: &Self) -> bool {
203        self.local_proto == other.local_proto
204            && self.local_addr == other.local_addr
205            && self.local_port == other.local_port
206    }
207}
208
209impl Display for ClientEndpoint {
210    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
211        if let Some(name) = self.description.as_ref() {
212            if !name.is_empty() {
213                write!(f, "[{}] ", name)?;
214            }
215        }
216        write!(f, "{}", self.as_url())
217    }
218}
219
220impl Endpoint for ClientEndpoint {
221    fn credentials(&self) -> String {
222        let mut s = String::new();
223        let username = self.username.as_deref().unwrap_or_default();
224        let password = self.password.as_deref().unwrap_or_default();
225        if !username.is_empty() {
226            s.push_str(&encode(username));
227        }
228        if !password.is_empty() {
229            s.push(':');
230            s.push_str(&encode(password));
231        }
232        if !s.is_empty() {
233            s.push('@');
234        }
235        s
236    }
237
238    fn as_url(&self) -> String {
239        match self.local_proto.try_into().unwrap() {
240            Protocol::OneC | Protocol::Minecraft | Protocol::Webdav => {
241                let credentials = self.credentials();
242                format!(
243                    "{}://{}{}",
244                    str_enum::<Protocol>(self.local_proto),
245                    credentials,
246                    self.local_addr
247                )
248            }
249            Protocol::Http
250            | Protocol::Https
251            | Protocol::Tcp
252            | Protocol::Udp
253            | Protocol::Rtsp
254            | Protocol::Rdp
255            | Protocol::Vnc
256            | Protocol::Ssh => {
257                let credentials = self.credentials();
258                format!(
259                    "{}://{}{}:{}{}",
260                    str_enum::<Protocol>(self.local_proto),
261                    credentials,
262                    self.local_addr,
263                    self.local_port,
264                    self.local_path
265                )
266            }
267        }
268    }
269}
270
271impl Endpoint for ServerEndpoint {
272    fn credentials(&self) -> String {
273        String::new()
274    }
275
276    fn as_url(&self) -> String {
277        format!(
278            "{}://{}:{}",
279            str_enum::<Protocol>(self.remote_proto),
280            self.remote_addr,
281            self.remote_port,
282        )
283    }
284}
285
286impl Display for ServerEndpoint {
287    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
288        let client = self.client.as_ref().unwrap();
289        if self.error.is_empty() {
290            write!(
291                f,
292                "{} -> {}://{}{}:{}{}",
293                client,
294                Protocol::try_from(self.remote_proto).unwrap(),
295                client.credentials(),
296                self.remote_addr,
297                self.remote_port,
298                client.local_path
299            )
300        } else {
301            write!(f, "{} -> {}", client, self.error)
302        }
303    }
304}
305
306impl AgentInfo {
307    pub fn is_support_server_control(&self) -> bool {
308        get_version_number(&self.version) >= get_version_number("2.1.1")
309    }
310
311    pub fn is_support_backpressure(&self) -> bool {
312        get_version_number(&self.version) >= get_version_number("2.2.0")
313    }
314
315    pub fn get_unique_id(&self) -> String {
316        if self.hwid.is_empty() {
317            self.agent_id.clone()
318        } else {
319            self.hwid.clone()
320        }
321    }
322}
323
324impl PartialEq for ServerEndpoint {
325    fn eq(&self, other: &Self) -> bool {
326        self.client == other.client
327    }
328}
329
330// New Protocol Buffers message reading and writing functions
331pub async fn read_message<T: AsyncRead + Unpin>(conn: &mut T) -> Result<message::Message> {
332    let mut buf = [0u8; std::mem::size_of::<u32>()];
333    conn.read_exact(&mut buf).await?;
334    let len = u32::from_le_bytes(buf) as usize;
335    if !(1..=1024 * 1024 * 10).contains(&len) {
336        bail!("Invalid message length: {}", len);
337    }
338    let mut buf = vec![0u8; len];
339    conn.read_exact(&mut buf).await?;
340
341    let proto_msg =
342        Message::decode(buf.as_slice()).context("Failed to decode Protocol Buffers message")?;
343
344    Ok(proto_msg.message.unwrap())
345}
346
347pub async fn write_message<T: AsyncWrite + Unpin>(
348    conn: &mut T,
349    msg: &message::Message,
350) -> Result<()> {
351    let proto_msg = Message {
352        message: Some(msg.clone()),
353    };
354
355    debug!("Sending proto message: {:?}", msg);
356
357    let mut buf = BytesMut::new();
358    proto_msg
359        .encode(&mut buf)
360        .context("Failed to encode Protocol Buffers message")?;
361
362    let len = buf.len() as u32;
363    let len_bytes = len.to_le_bytes();
364
365    conn.write_all(&len_bytes).await?;
366    conn.write_all(&buf).await?;
367    conn.flush().await?;
368    Ok(())
369}
370
371impl FairGroup for message::Message {
372    fn group_id(&self) -> Option<u32> {
373        match self {
374            message::Message::DataChannelData(lhs) => Some(lhs.channel_id),
375            message::Message::DataChannelDataUdp(lhs) => Some(lhs.channel_id),
376            message::Message::DataChannelEof(lhs) => Some(lhs.channel_id),
377            message::Message::DataChannelAck(lhs) => Some(lhs.channel_id),
378            _ => None,
379        }
380    }
381
382    fn get_size(&self) -> Option<usize> {
383        match self {
384            message::Message::DataChannelData(data) => Some(data.data.len()),
385            message::Message::DataChannelDataUdp(data) => Some(data.data.len()),
386            _ => None, // Control messages have no size
387        }
388    }
389}
390
391pub type UdpPacketLen = u16; // `u16` should be enough for any practical UDP traffic on the Internet
392                             //
393#[derive(Deserialize, Serialize, Debug)]
394pub struct UdpHeader {
395    from: std::net::SocketAddr,
396    len: UdpPacketLen,
397}
398
399#[derive(Debug)]
400pub struct UdpTraffic {
401    pub from: std::net::SocketAddr,
402    pub data: Bytes,
403}
404
405impl UdpTraffic {
406    pub async fn write<T: AsyncWrite + Unpin>(&self, writer: &mut T) -> Result<()> {
407        let hdr = UdpHeader {
408            from: self.from,
409            len: self.data.len() as UdpPacketLen,
410        };
411
412        let v = bincode::serde::encode_to_vec(&hdr, bincode::config::legacy()).unwrap();
413
414        trace!("Write {:?} of length {}", hdr, v.len());
415        writer.write_u8(v.len() as u8).await?;
416        writer.write_all(&v).await?;
417
418        writer.write_all(&self.data).await?;
419
420        Ok(())
421    }
422
423    #[allow(dead_code)]
424    pub async fn write_slice<T: AsyncWrite + Unpin>(
425        writer: &mut T,
426        from: std::net::SocketAddr,
427        data: &[u8],
428    ) -> Result<()> {
429        let hdr = UdpHeader {
430            from,
431            len: data.len() as UdpPacketLen,
432        };
433
434        let v = bincode::serde::encode_to_vec(&hdr, bincode::config::legacy()).unwrap();
435
436        trace!("Write {:?} of length {}", hdr, v.len());
437        writer.write_u8(v.len() as u8).await?;
438        writer.write_all(&v).await?;
439
440        writer.write_all(data).await?;
441
442        Ok(())
443    }
444
445    pub async fn read<T: AsyncRead + Unpin>(reader: &mut T, hdr_len: u8) -> Result<UdpTraffic> {
446        let mut buf = vec![0; hdr_len as usize];
447        reader
448            .read_exact(&mut buf)
449            .await
450            .with_context(|| "Failed to read udp header")?;
451
452        let hdr: UdpHeader = bincode::serde::decode_from_slice(&buf, bincode::config::legacy())
453            .with_context(|| "Failed to deserialize UdpHeader")?
454            .0;
455
456        trace!("hdr {:?}", hdr);
457
458        let mut data = BytesMut::new();
459        data.resize(hdr.len as usize, 0);
460        reader.read_exact(&mut data).await?;
461
462        Ok(UdpTraffic {
463            from: hdr.from,
464            data: data.freeze(),
465        })
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472
473    // HYPOTHESIS: a lease collision comes back as HANDSHAKE_FAILED, so a
474    // consumer cannot tell the temporary refusal (the only failure path of
475    // acquire_lease is the eviction timeout in drop_lease) from a permanent
476    // one - 30 fatal-looking failures in 2h on 21.08 (CLOUDPUB-1339).
477    // SOLUTION: a dedicated ErrorKind::LeaseBusy plus one exhaustive
478    // is_fatal() classifier that both client receive loops share.
479    // STATUS: FIXED - see CLOUDPUB-1339
480    #[test]
481    fn lease_busy_is_retryable_not_fatal() {
482        assert!(ErrorKind::Fatal.is_fatal());
483        assert!(ErrorKind::AuthFailed.is_fatal());
484        assert!(
485            !ErrorKind::LeaseBusy.is_fatal(),
486            "a busy lease is temporary by construction; treating it as fatal \
487             is the CLOUDPUB-1339 incident"
488        );
489        assert!(!ErrorKind::HandshakeFailed.is_fatal());
490    }
491}