1#[cfg(feature = "server")]
21pub mod server;
22
23#[cfg(feature = "client")]
24pub mod client;
25
26pub mod voice;
27
28use std::
29{
30 fs::File,
31 path::PathBuf,
32 net::TcpStream,
33 io::{ Read, Write },
34 sync::
35 {
36 Arc,
37 Mutex,
38 LazyLock,
39 },
40};
41
42use wincode::{ SchemaWrite, SchemaRead };
43
44use sha2::Sha256;
45
46use dashmap::DashMap;
47
48use why2::consts;
49
50use crate::
51{
52 crypto,
53 consts::
54 {
55 Streams,
56 self as chat_consts,
57 },
58};
59
60#[cfg(feature = "client")]
61use crate::options;
62
63#[cfg(feature = "server")]
64use std::time::{ Instant, Duration };
65
66#[cfg(feature = "server")]
67use crate::config;
68
69#[derive(SchemaWrite, SchemaRead, PartialEq, Clone)]
71pub enum MessageCode {
73 KeyExchange, Rekey, Welcome, Disconnect, Username, PasswordL, PasswordR, Accept, Join, Leave, List, PrivateMessage, PrivateMessageBack, SpamWarning, RegisterDisabled, Version, Channel, Voice, ChannelJoin, ChannelLeave, VoiceClients, Upload, Download, Uploaded, Files, InvalidUsage, InvalidFeature, KeepAlive, }
102
103#[derive(SchemaWrite, SchemaRead, Clone)]
104pub struct MessageColors {
106 pub username_color: Option<u8>, pub message_color: Option<u8>, }
109
110#[derive(SchemaWrite, SchemaRead, Clone)]
111pub struct FilePayload {
113 pub uid: u64, pub data: Option<Vec<u8>>, pub size: Option<u64>, pub filename: Option<String>, pub hash: Option<[u8; 32]>, }
119
120#[derive(SchemaWrite, SchemaRead, Clone)]
121pub struct MessagePacket {
123 pub text: Option<String>, pub username: Option<String>, pub id: Option<usize>, pub code: Option<MessageCode>, pub colors: MessageColors, pub seq: usize, pub file: Option<FilePayload>, }
131
132pub struct ActiveFileshare {
134 pub file: File, pub size: u64, pub current_size: u64, pub hash: [u8; 32], pub hasher: Sha256, pub filename: String, #[cfg(feature = "server")] pub client_id: usize, }
142
143pub static ACTIVE_FILESHARES: LazyLock<DashMap<u64, ActiveFileshare>> = LazyLock::new(|| DashMap::new()); impl Default for MessagePacket {
149 fn default() -> Self
150 {
151 Self
152 {
153 text: None,
154 username: None,
155 id: None,
156 code: None,
157 colors: MessageColors
158 {
159 username_color: None,
160 message_color: None,
161 },
162 seq: 0,
163 file: None,
164 }
165 }
166}
167
168impl Default for FilePayload
169{
170 fn default() -> Self
171 {
172 Self
173 {
174 uid: 0,
175 data: None,
176 size: None,
177 filename: None,
178 hash: None,
179 }
180 }
181}
182
183fn obfuscate_data(mut data: Vec<u8>) -> Vec<u8> {
187 for (i, byte) in data.iter_mut().enumerate()
188 {
189 *byte ^= chat_consts::OBFUSCATION_KEY[i % chat_consts::OBFUSCATION_KEY.len()];
191 }
192
193 data
194}
195
196pub fn send(stream: &mut TcpStream, mut packet: MessagePacket, keys: Option<&chat_consts::SharedKeys>) {
199 #[cfg(feature = "client")]
201 {
202 packet.seq = options::get_seq() + 1;
203 options::set_seq(packet.seq);
204 }
205
206 #[cfg(feature = "server")]
208 {
209 let peer_addr = stream.peer_addr().ok();
210 if peer_addr.is_some() && let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr.unwrap())
211 {
212 if conn.is_authenticated()
213 {
214 packet.seq = conn.server_seq().unwrap() + 1;
215 *conn.server_seq_mut().unwrap() = packet.seq;
216 }
217 }
218 }
219
220 let packet_bytes = wincode::serialize(&packet).expect("Encoding packet failed");
222
223 let mut final_bytes = if let Some(keys) = keys
224 {
225 crypto::encrypt_packet::<{ consts::DEFAULT_GRID_WIDTH }, { consts::DEFAULT_GRID_HEIGHT }>(packet_bytes, keys)
226 } else
227 {
228 obfuscate_data(packet_bytes) };
230
231 let packet_len = final_bytes.len();
233 let mut transmission_packet = Vec::with_capacity(4 + packet_len);
234 transmission_packet.extend_from_slice(&(packet_len as u32).to_be_bytes());
235 transmission_packet.append(&mut final_bytes);
236
237 let _ = stream.write_all(&transmission_packet);
239 stream.flush().expect("Flushing stream failed");
240}
241
242pub fn receive(streams: &mut Streams, keys: Option<&chat_consts::SharedKeys>) -> Option<MessagePacket>
243{
244 #[cfg(feature = "server")]
246 let max_packet_size: usize;
247
248 #[cfg(feature = "server")]
250 let spam_protection = config::read_config::<bool>("spam_protection");
251
252 #[cfg(feature = "server")]
253 let peer_addr = streams.0.peer_addr().ok()?; #[cfg(feature = "server")]
257 {
258 let authenticated = server::CONNECTIONS.get(&peer_addr)
260 .map(|conn| conn.is_authenticated())
261 .unwrap_or(false);
262
263 max_packet_size = if !spam_protection && authenticated
265 {
266 usize::MAX
267 } else {
269 config::read_config("max_packet_size")
270 };
271 }
272
273 let mut len_buf = [0u8; 4];
275 if streams.0.read_exact(&mut len_buf).is_err() {
277 #[cfg(feature = "server")]
278 server::remove_connection(&peer_addr, false, Some("length"));
279 return None;
280 }
281 let len = u32::from_be_bytes(len_buf) as usize;
282
283 #[cfg(feature = "server")]
285 if len > max_packet_size
286 {
287 server::remove_connection(&peer_addr, true, Some("length"));
288 return None;
289 }
290
291 let mut decoded_packet = vec![0u8; len];
293 if streams.0.read_exact(&mut decoded_packet).is_err() {
295 #[cfg(feature = "server")]
296 server::remove_connection(&peer_addr, false, Some("length"));
297 return None;
298 }
299
300 if let Some(keys) = keys
302 {
303 decoded_packet = match crypto::decrypt_packet::<{ consts::DEFAULT_GRID_WIDTH }, { consts::DEFAULT_GRID_HEIGHT }>(decoded_packet, keys)
304 {
305 Some(d) => d,
306 None => {
308 #[cfg(feature = "server")]
310 log::warn!("HMAC verification failed: {}", peer_addr);
311
312 return None;
313 }
314 }
315 } else
316 {
317 decoded_packet = obfuscate_data(decoded_packet); }
319
320 match wincode::deserialize::<MessagePacket>(&decoded_packet)
322 {
323 Ok(packet) =>
324 {
325 #[cfg(feature = "server")]
327 {
328 let mut spam_warning = false;
330 let mut shared_key = None;
331 let mut disconnect = false;
332 let mut grace = true;
333
334 if let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr)
335 {
336 if packet.code != Some(MessageCode::KeepAlive) &&
338 packet.code != Some(MessageCode::KeyExchange) &&
339 packet.file.is_none()
340 {
341 if let Some(text) = &packet.text && conn.is_authenticated()
343 {
344 disconnect = text.len() > config::read_config("max_message_length");
345 }
346
347 if !disconnect && config::read_config("spam_protection") && conn.is_authenticated() &&
348 Instant::now().duration_since(*conn.last_activity()) <
349 Duration::from_millis(config::read_config::<u64>("min_message_delay"))
350 {
351 *conn.spam_violations_mut().unwrap() += 1;
353
354 spam_warning = true;
356 shared_key = conn.keys().cloned();
357
358 disconnect = *conn.spam_violations().unwrap() > config::read_config::<usize>("max_message_delay_violations");
360 }
361
362 *conn.last_activity_mut() = Instant::now(); }
364
365 if packet.seq > *conn.seq() {
368 *conn.seq_mut() = packet.seq;
370 } else
371 {
372 grace = false;
374 disconnect = true;
375 }
376 drop(conn); if spam_warning
380 {
381 server::send_code(&mut streams.1.lock().unwrap(), None, MessageCode::SpamWarning, shared_key.as_ref());
382 }
383
384 if disconnect
386 {
387 server::remove_connection(&peer_addr, grace, Some(if !grace { "SEQ" } else { "SPAM" }));
388 return None;
389 }
390 }
391 }
392
393 #[cfg(feature = "client")] {
396 if packet.seq > options::get_server_seq() || options::get_server_seq() == 0 || packet.code == Some(MessageCode::Disconnect) {
398 options::set_server_seq(packet.seq);
400 } else {
402 return None;
403 }
404 }
405
406 return Some(packet);
407 },
408
409 Err(_) =>
410 {
411 #[cfg(feature = "server")]
413 server::remove_connection(&peer_addr, false, Some("packet"));
414
415 return None;
416 }
417 }
418}
419
420pub fn send_file (
422 path: PathBuf,
423 write_stream: Arc<Mutex<TcpStream>>,
424 uid: u64,
425 code: MessageCode,
426 keys: Option<&chat_consts::SharedKeys>
427)
428{
429 let mut file = File::open(path).expect("Cannot open file for upload");
430 let mut buffer = vec![0; chat_consts::UPLOAD_CHUNK_SIZE];
431
432 loop
434 {
435 match file.read(&mut buffer)
436 {
437 Ok(0) => break, Ok(bytes) =>
439 {
440 send(&mut write_stream.lock().unwrap(), MessagePacket
442 {
443 code: Some(code.clone()),
444 file: Some(FilePayload
445 {
446 uid,
447 data: Some(buffer[..bytes].to_vec()),
448 ..Default::default()
449 }),
450 ..Default::default()
451 }, keys);
452 },
453 Err(_) => {}, }
455 }
456}