Skip to main content

dvrip_rs/
protocol.rs

1use crate::error::{DVRIPError, Result};
2use byteorder::{ByteOrder, LittleEndian};
3use serde_json::Value;
4use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
5
6pub struct PacketHeader {
7    pub head: u8,
8    pub version: u8,
9    pub session: u32,
10    pub packet_count: u32,
11    pub msg_id: u16,
12    pub data_len: u32,
13}
14
15impl PacketHeader {
16    pub const SIZE: usize = 20;
17
18    pub fn encode(&self) -> Vec<u8> {
19        let mut buf = vec![0u8; Self::SIZE];
20        buf[0] = self.head;
21        buf[1] = self.version;
22        LittleEndian::write_u32(&mut buf[4..8], self.session);
23        LittleEndian::write_u32(&mut buf[8..12], self.packet_count);
24        LittleEndian::write_u16(&mut buf[14..16], self.msg_id);
25        LittleEndian::write_u32(&mut buf[16..20], self.data_len);
26        buf
27    }
28
29    pub fn decode(data: &[u8]) -> Result<Self> {
30        if data.len() < Self::SIZE {
31            return Err(DVRIPError::ProtocolError("Header too small".to_string()));
32        }
33        Ok(Self {
34            head: data[0],
35            version: data[1],
36            session: LittleEndian::read_u32(&data[4..8]),
37            packet_count: LittleEndian::read_u32(&data[8..12]),
38            msg_id: LittleEndian::read_u16(&data[14..16]),
39            data_len: LittleEndian::read_u32(&data[16..20]),
40        })
41    }
42}
43
44pub async fn send_packet<W: AsyncWrite + Unpin>(
45    writer: &mut W,
46    session: u32,
47    packet_count: u32,
48    msg_id: u16,
49    data: &[u8],
50    version: u8,
51) -> Result<()> {
52    let tail: &[u8] = if version == 0 { b"\x0a\x00" } else { b"\x00" };
53    let data_len = (data.len() + tail.len()) as u32;
54
55    let header = PacketHeader {
56        head: 255,
57        version,
58        session,
59        packet_count,
60        msg_id,
61        data_len,
62    };
63
64    let mut packet = header.encode();
65    packet.extend_from_slice(data);
66    packet.extend_from_slice(tail);
67
68    writer.write_all(&packet).await?;
69    writer.flush().await?;
70    Ok(())
71}
72
73pub async fn receive_packet_header<R: AsyncRead + Unpin>(reader: &mut R) -> Result<PacketHeader> {
74    let mut buf = vec![0u8; PacketHeader::SIZE];
75    let mut received = 0;
76
77    // Read header in parts to avoid issues with data not immediately available
78    while received < PacketHeader::SIZE {
79        match reader.read(&mut buf[received..]).await {
80            Ok(0) => {
81                return Err(DVRIPError::ConnectionError(
82                    "Connection closed by peer".to_string(),
83                ));
84            }
85            Ok(n) => {
86                received += n;
87            }
88            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
89                return Err(DVRIPError::ConnectionError(
90                    "Connection closed unexpectedly".to_string(),
91                ));
92            }
93            Err(e) => {
94                return Err(DVRIPError::IoError(e));
95            }
96        }
97    }
98
99    PacketHeader::decode(&buf)
100}
101
102pub async fn receive_data<R: AsyncRead + Unpin>(
103    reader: &mut R,
104    length: usize,
105    timeout: tokio::time::Duration,
106) -> Result<Vec<u8>> {
107    let mut buf = vec![0u8; length];
108    let mut received = 0;
109
110    while received < length {
111        let remaining = length - received;
112        let result = tokio::time::timeout(
113            timeout,
114            reader.read(&mut buf[received..received + remaining]),
115        )
116        .await;
117
118        let chunk = match result {
119            Ok(Ok(n)) => n,
120            Ok(Err(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
121                return Err(DVRIPError::ConnectionError(
122                    "Connection closed unexpectedly during read".to_string(),
123                ));
124            }
125            Ok(Err(e)) => {
126                return Err(DVRIPError::IoError(e));
127            }
128            Err(_) => {
129                return Err(DVRIPError::ConnectionError(
130                    "Timeout receiving data".to_string(),
131                ));
132            }
133        };
134
135        if chunk == 0 {
136            return Err(DVRIPError::ConnectionError(
137                "Connection closed by peer".to_string(),
138            ));
139        }
140        received += chunk;
141    }
142
143    Ok(buf)
144}
145
146pub async fn receive_json<R: AsyncRead + Unpin>(
147    reader: &mut R,
148    length: usize,
149    timeout: tokio::time::Duration,
150) -> Result<Value> {
151    let data = receive_data(reader, length, timeout).await?;
152    // Remove tail (\x0a\x00 or \x00)
153    let json_data =
154        if data.len() >= 2 && data[data.len() - 2] == 0x0a && data[data.len() - 1] == 0x00 {
155            &data[..data.len() - 2]
156        } else if data.len() >= 1 && data[data.len() - 1] == 0x00 {
157            &data[..data.len() - 1]
158        } else {
159            &data
160        };
161
162    let json_str = String::from_utf8_lossy(json_data);
163    serde_json::from_str(&json_str)
164        .map_err(|e| DVRIPError::SerializationError(format!("Error parsing JSON: {}", e)))
165}
166
167pub fn sofia_hash(password: &str) -> String {
168    let digest = md5::compute(password.as_bytes());
169
170    let chars: Vec<char> = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
171        .chars()
172        .collect();
173
174    let mut result = String::new();
175    for i in (0..digest.len()).step_by(2) {
176        if i + 1 < digest.len() {
177            let sum = digest[i] as usize + digest[i + 1] as usize;
178            result.push(chars[sum % 62]);
179        }
180    }
181    result
182}