arcly_stream/protocol/srt/
handshake.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum HandshakeType {
12 Induction,
14 Conclusion,
16 WaveAHand,
18 Agreement,
20 Other(u32),
22}
23
24impl HandshakeType {
25 fn from_u32(v: u32) -> HandshakeType {
26 match v {
27 1 => HandshakeType::Induction,
28 0xFFFF_FFFF => HandshakeType::Conclusion,
29 0 => HandshakeType::WaveAHand,
30 0xFFFF_FFFE => HandshakeType::Agreement,
31 other => HandshakeType::Other(other),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct SrtHandshake {
39 pub version: u32,
41 pub encryption: u16,
44 pub initial_sequence: u32,
46 pub handshake_type: HandshakeType,
48 pub socket_id: u32,
50 pub cookie: u32,
52}
53
54impl SrtHandshake {
55 const PAYLOAD: usize = 16;
58
59 pub fn parse(datagram: &[u8]) -> Option<SrtHandshake> {
62 let b = datagram.get(Self::PAYLOAD..)?;
63 if b.len() < 32 {
64 return None;
65 }
66 let w = |i: usize| u32::from_be_bytes([b[i], b[i + 1], b[i + 2], b[i + 3]]);
67 Some(SrtHandshake {
68 version: w(0),
69 encryption: u16::from_be_bytes([b[4], b[5]]),
70 initial_sequence: w(8),
71 handshake_type: HandshakeType::from_u32(w(20)),
72 socket_id: w(24),
73 cookie: w(28),
74 })
75 }
76
77 pub fn wants_encryption(&self) -> bool {
79 self.encryption != 0
80 }
81}
82
83fn syn_cookie(socket_id: u32) -> u32 {
88 socket_id
89 .rotate_left(13)
90 .wrapping_mul(0x9E37_79B1)
91 .wrapping_add(0x5247_5421)
92}
93
94pub fn respond(datagram: &[u8]) -> Option<Vec<u8>> {
101 let hs = SrtHandshake::parse(datagram)?;
102 if hs.wants_encryption() {
103 return None; }
105 let mut reply = datagram.to_vec();
106 let cookie = match hs.handshake_type {
107 HandshakeType::Induction => syn_cookie(hs.socket_id),
108 HandshakeType::Conclusion => hs.cookie,
109 _ => return None,
110 };
111 let at = SrtHandshake::PAYLOAD + 28;
113 reply
114 .get_mut(at..at + 4)?
115 .copy_from_slice(&cookie.to_be_bytes());
116 Some(reply)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 fn handshake_datagram(req_type: u32, encryption: u16) -> Vec<u8> {
125 let mut d = vec![0u8; 16]; d[0] = 0x80; let mut body = vec![0u8; 32];
128 body[0..4].copy_from_slice(&5u32.to_be_bytes()); body[4..6].copy_from_slice(&encryption.to_be_bytes());
130 body[20..24].copy_from_slice(&req_type.to_be_bytes());
131 body[24..28].copy_from_slice(&0xABCD_1234u32.to_be_bytes()); d.extend_from_slice(&body);
133 d
134 }
135
136 #[test]
137 fn parses_induction_handshake() {
138 let d = handshake_datagram(1, 0);
139 let hs = SrtHandshake::parse(&d).unwrap();
140 assert_eq!(hs.version, 5);
141 assert_eq!(hs.handshake_type, HandshakeType::Induction);
142 assert_eq!(hs.socket_id, 0xABCD_1234);
143 assert!(!hs.wants_encryption());
144 }
145
146 #[test]
147 fn induction_response_installs_nonzero_cookie() {
148 let d = handshake_datagram(1, 0);
149 let reply = respond(&d).unwrap();
150 let parsed = SrtHandshake::parse(&reply).unwrap();
151 assert_ne!(parsed.cookie, 0, "cookie installed in induction response");
152 }
153
154 #[test]
155 fn encrypted_handshake_is_rejected() {
156 let d = handshake_datagram(1, 0x0002);
157 assert!(respond(&d).is_none());
158 }
159
160 #[test]
161 fn non_handshake_request_type_has_no_response() {
162 let d = handshake_datagram(0, 0); assert!(respond(&d).is_none());
164 }
165}