File
Blob: firmware/vendor/str0m/src/io/mod.rs
| 1 | #![allow(clippy::manual_range_contains)] |
| 2 | #![allow(clippy::new_without_default)] |
| 3 | |
| 4 | use std::convert::TryFrom; |
| 5 | use std::fmt; |
| 6 | use std::io; |
| 7 | use std::net::SocketAddr; |
| 8 | |
| 9 | use serde::{Deserialize, Serialize}; |
| 10 | |
| 11 | pub use is::stun::StunMessage; |
| 12 | |
| 13 | pub use str0m_proto::DATAGRAM_MTU_TARGET; |
| 14 | pub use str0m_proto::DATAGRAM_MTU_TARGET_MAX; |
| 15 | pub use str0m_proto::DATAGRAM_MTU_TARGET_MIN; |
| 16 | pub use str0m_proto::DATAGRAM_MTU_WARN; |
| 17 | |
| 18 | /// Max UDP packet size |
| 19 | pub(crate) const DATAGRAM_MAX_PACKET_SIZE: usize = 2000; |
| 20 | |
| 21 | /// Max expected RTP header over, with full extensions etc. |
| 22 | pub const MAX_RTP_OVERHEAD: usize = 80; |
| 23 | |
| 24 | /// Max expected DTLS record overhead (record header + explicit nonce + AEAD |
| 25 | /// auth tag), worst case across DTLS 1.2/1.3 and common AEAD ciphers |
| 26 | /// (AES-GCM, AES-CCM, CHACHA20-POLY1305). |
| 27 | pub const MAX_DTLS_OVERHEAD: usize = 48; |
| 28 | |
| 29 | mod error; |
| 30 | pub use self::error::NetError; |
| 31 | #[allow(unused_imports)] |
| 32 | pub use self::error::StunError; |
| 33 | |
| 34 | pub use str0m_proto::{DatagramSend, Protocol, TcpType, Transmit}; |
| 35 | |
| 36 | #[derive(Debug, Serialize, Deserialize)] |
| 37 | /// Received incoming data. |
| 38 | pub struct Receive<'a> { |
| 39 | /// The protocol the socket this received data originated from is using. |
| 40 | pub proto: Protocol, |
| 41 | |
| 42 | /// The socket this received data originated from. |
| 43 | pub source: SocketAddr, |
| 44 | |
| 45 | /// The destination ip of the datagram. |
| 46 | pub destination: SocketAddr, |
| 47 | |
| 48 | /// Parsed contents of the datagram. |
| 49 | #[serde(borrow)] |
| 50 | pub contents: DatagramRecv<'a>, |
| 51 | } |
| 52 | |
| 53 | impl<'a> Receive<'a> { |
| 54 | /// Creates a new instance by trying to parse the contents of `buf`. |
| 55 | pub fn new( |
| 56 | proto: Protocol, |
| 57 | source: SocketAddr, |
| 58 | destination: SocketAddr, |
| 59 | buf: &'a [u8], |
| 60 | ) -> Result<Self, NetError> { |
| 61 | let contents = DatagramRecv::try_from(buf)?; |
| 62 | Ok(Receive { |
| 63 | proto, |
| 64 | source, |
| 65 | destination, |
| 66 | contents, |
| 67 | }) |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /// Wrapper for a parsed payload to be received. |
| 72 | #[derive(Serialize, Deserialize)] |
| 73 | pub struct DatagramRecv<'a> { |
| 74 | #[serde(borrow)] |
| 75 | pub(crate) inner: DatagramRecvInner<'a>, |
| 76 | } |
| 77 | |
| 78 | #[allow(clippy::large_enum_variant)] // We purposely don't want to allocate. |
| 79 | #[derive(Serialize, Deserialize)] |
| 80 | pub(crate) enum DatagramRecvInner<'a> { |
| 81 | Stun(StunMessage<'a>), |
| 82 | Dtls(&'a [u8]), |
| 83 | Rtp(&'a [u8]), |
| 84 | Rtcp(&'a [u8]), |
| 85 | } |
| 86 | |
| 87 | impl<'a> TryFrom<&'a [u8]> for DatagramRecv<'a> { |
| 88 | type Error = NetError; |
| 89 | |
| 90 | fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { |
| 91 | use DatagramRecvInner::*; |
| 92 | |
| 93 | let kind = MultiplexKind::try_from(value)?; |
| 94 | |
| 95 | let inner = match kind { |
| 96 | MultiplexKind::Stun => Stun(StunMessage::parse(value)?), |
| 97 | MultiplexKind::Dtls => Dtls(value), |
| 98 | MultiplexKind::Rtp => Rtp(value), |
| 99 | MultiplexKind::Rtcp => Rtcp(value), |
| 100 | }; |
| 101 | |
| 102 | Ok(DatagramRecv { inner }) |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | #[derive(Debug, Clone, Copy, PartialEq)] |
| 107 | pub(crate) enum MultiplexKind { |
| 108 | Stun, |
| 109 | Dtls, |
| 110 | Rtp, |
| 111 | Rtcp, |
| 112 | } |
| 113 | |
| 114 | impl<'a> TryFrom<&'a [u8]> for MultiplexKind { |
| 115 | type Error = io::Error; |
| 116 | |
| 117 | fn try_from(value: &'a [u8]) -> Result<Self, io::Error> { |
| 118 | if value.is_empty() { |
| 119 | return Err(io::Error::new(io::ErrorKind::InvalidData, "Empty datagram")); |
| 120 | } |
| 121 | |
| 122 | let byte0 = value[0]; |
| 123 | let len = value.len(); |
| 124 | |
| 125 | if byte0 < 2 && len >= 20 { |
| 126 | Ok(MultiplexKind::Stun) |
| 127 | } else if byte0 >= 20 && byte0 < 64 { |
| 128 | Ok(MultiplexKind::Dtls) |
| 129 | } else if byte0 >= 128 && byte0 < 192 && len > 2 { |
| 130 | let byte1 = value[1]; |
| 131 | let payload_type = byte1 & 0x7f; |
| 132 | |
| 133 | Ok(if payload_type < 64 { |
| 134 | // This is kinda novel, and probably breaks, but... |
| 135 | // we can use the < 64 pt as an escape hatch if we run out |
| 136 | // of dynamic numbers >= 96 |
| 137 | // https://bugs.chromium.org/p/webrtc/issues/detail?id=12194 |
| 138 | MultiplexKind::Rtp |
| 139 | } else if payload_type >= 64 && payload_type < 96 { |
| 140 | MultiplexKind::Rtcp |
| 141 | } else { |
| 142 | MultiplexKind::Rtp |
| 143 | }) |
| 144 | } else { |
| 145 | Err(io::Error::new( |
| 146 | io::ErrorKind::InvalidData, |
| 147 | "Unknown datagram", |
| 148 | )) |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | impl<'a> TryFrom<&'a Transmit> for Receive<'a> { |
| 154 | type Error = NetError; |
| 155 | |
| 156 | fn try_from(t: &'a Transmit) -> Result<Self, Self::Error> { |
| 157 | Ok(Receive { |
| 158 | proto: t.proto, |
| 159 | source: t.source, |
| 160 | destination: t.destination, |
| 161 | contents: DatagramRecv::try_from(&t.contents[..])?, |
| 162 | }) |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | impl fmt::Debug for DatagramRecv<'_> { |
| 167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 168 | self.inner.fmt(f) |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | impl fmt::Debug for DatagramRecvInner<'_> { |
| 173 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 174 | match self { |
| 175 | Self::Stun(v) => f.debug_tuple("Stun").field(v).finish(), |
| 176 | Self::Dtls(v) => write!(f, "Dtls(len: {})", v.len()), |
| 177 | Self::Rtp(v) => write!(f, "Rtp(len: {})", v.len()), |
| 178 | Self::Rtcp(v) => write!(f, "Rtcp(len: {})", v.len()), |
| 179 | } |
| 180 | } |
| 181 | // |
| 182 | } |