scuffle_rtmp/handshake/
simple.rs

1//! Simple Handshake Server
2
3use std::io::{self, Seek, Write};
4
5use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
6use bytes::Bytes;
7use rand::Rng;
8use scuffle_bytes_util::BytesCursorExt;
9
10use super::{RTMP_HANDSHAKE_SIZE, RtmpVersion, ServerHandshakeState, TIME_VERSION_LENGTH, current_time};
11
12/// Simple Handshake Server
13///
14/// Defined by:
15/// - Legacy RTMP spec, 5.2
16pub struct SimpleHandshakeServer {
17    version: RtmpVersion,
18    requested_version: RtmpVersion,
19    state: ServerHandshakeState,
20    c1_bytes: Bytes,
21    c1_timestamp: u32,
22}
23
24impl Default for SimpleHandshakeServer {
25    fn default() -> Self {
26        Self {
27            state: ServerHandshakeState::ReadC0C1,
28            c1_bytes: Bytes::new(),
29            c1_timestamp: 0,
30            version: RtmpVersion::Version3,
31            requested_version: RtmpVersion(0),
32        }
33    }
34}
35
36impl SimpleHandshakeServer {
37    /// Returns true if the handshake is finished.
38    pub fn is_finished(&self) -> bool {
39        self.state == ServerHandshakeState::Finish
40    }
41
42    /// Perform the handshake, writing to the output and reading from the input.
43    pub fn handshake(&mut self, input: &mut io::Cursor<Bytes>, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
44        match self.state {
45            ServerHandshakeState::ReadC0C1 => {
46                self.read_c0(input)?;
47                self.read_c1(input)?;
48                self.write_s0(output)?;
49                self.write_s1(output)?;
50                self.write_s2(output)?;
51                self.state = ServerHandshakeState::ReadC2;
52            }
53            ServerHandshakeState::ReadC2 => {
54                self.read_c2(input)?;
55                self.state = ServerHandshakeState::Finish;
56            }
57            ServerHandshakeState::Finish => {}
58        }
59
60        Ok(())
61    }
62
63    fn read_c0(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
64        // Version (8 bits): In C0, this field identifies the RTMP version
65        // requested by the client.
66        self.requested_version = RtmpVersion::from(input.read_u8()?);
67
68        // We only support version 3 for now.
69        // Therefore we set the version to 3.
70        self.version = RtmpVersion::Version3;
71
72        Ok(())
73    }
74
75    fn read_c1(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
76        // Time (4 bytes): This field contains a timestamp, which SHOULD be
77        // used as the epoch for all future chunks sent from this endpoint.
78        // This may be 0, or some arbitrary value. To synchronize multiple
79        // chunkstreams, the endpoint may wish to send the current value of
80        // the other chunkstream’s timestamp.
81        self.c1_timestamp = input.read_u32::<BigEndian>()?;
82
83        // Zero (4 bytes): This field MUST be all 0s.
84        input.read_u32::<BigEndian>()?;
85
86        // Random data (1528 bytes): This field can contain any arbitrary
87        // values. Since each endpoint has to distinguish between the
88        // response to the handshake it has initiated and the handshake
89        // initiated by its peer,this data SHOULD send something sufficiently
90        // random. But there is no need for cryptographically-secure
91        // randomness, or even dynamic values.
92        self.c1_bytes = input.extract_bytes(RTMP_HANDSHAKE_SIZE - TIME_VERSION_LENGTH)?;
93
94        Ok(())
95    }
96
97    fn read_c2(&mut self, input: &mut io::Cursor<Bytes>) -> Result<(), crate::error::RtmpError> {
98        // We don't care too much about the data in C2, so we just read it
99        // and discard it.
100        //
101        // We should technically check that the timestamp is the same as
102        // the one we sent in S1, but we don't care. And that the random
103        // data is the same as the one we sent in S2, but we don't care.
104        // Some clients are not strict to spec and send different data.
105        //
106        // We can just ignore it and not be super strict.
107        input.seek_relative(RTMP_HANDSHAKE_SIZE as i64)?;
108
109        Ok(())
110    }
111
112    /// Defined in RTMP Specification 1.0 - 5.2.2
113    fn write_s0(&mut self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
114        // Version (8 bits): In S0, this field identifies the RTMP
115        // version selected by the server. The version defined by this
116        // specification is 3. A server that does not recognize the
117        // client’s requested version SHOULD respond with 3. The client MAY
118        // choose to degrade to version 3, or to abandon the handshake.
119        output.write_u8(self.version.0)?;
120
121        Ok(())
122    }
123
124    /// Defined in RTMP Specification 1.0 - 5.2.3
125    fn write_s1(&mut self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
126        // Time (4 bytes): This field contains a timestamp, which SHOULD be
127        // used as the epoch for all future chunks sent from this endpoint.
128        // This may be 0, or some arbitrary value. To synchronize multiple
129        // chunkstreams, the endpoint may wish to send the current value of
130        // the other chunkstream’s timestamp.
131        output.write_u32::<BigEndian>(current_time())?;
132
133        // Zero(4 bytes): This field MUST be all 0s.
134        output.write_u32::<BigEndian>(0)?;
135
136        // Random data (1528 bytes): This field can contain any arbitrary
137        // values. Since each endpoint has to distinguish between the
138        // response to the handshake it has initiated and the handshake
139        // initiated by its peer,this data SHOULD send something sufficiently
140        // random. But there is no need for cryptographically-secure
141        // randomness, or even dynamic values.
142        let mut rng = rand::rng();
143        for _ in 0..1528 {
144            output.write_u8(rng.random())?;
145        }
146
147        Ok(())
148    }
149
150    fn write_s2(&mut self, output: &mut Vec<u8>) -> Result<(), crate::error::RtmpError> {
151        // Time (4 bytes): This field MUST contain the timestamp sent by the C1 (for
152        // S2).
153        output.write_u32::<BigEndian>(self.c1_timestamp)?;
154
155        // Time2 (4 bytes): This field MUST contain the timestamp at which the
156        // previous packet(s1 or c1) sent by the peer was read.
157        output.write_u32::<BigEndian>(current_time())?;
158
159        // Random echo (1528 bytes): This field MUST contain the random data
160        // field sent by the peer in S1 (for C2) or S2 (for C1). Either peer
161        // can use the time and time2 fields together with the current
162        // timestamp as a quick estimate of the bandwidth and/or latency of
163        // the connection, but this is unlikely to be useful.
164        output.write_all(&self.c1_bytes[..])?;
165
166        Ok(())
167    }
168}