#[cfg(feature = "server")]
pub mod server;
#[cfg(feature = "client")]
pub mod client;
pub mod voice;
use std::
{
fs::File,
path::PathBuf,
net::TcpStream,
io::{ Read, Write },
};
use wincode::{ SchemaWrite, SchemaRead };
use why2::consts;
use crate::
{
crypto,
consts::
{
Streams,
self as chat_consts,
},
};
#[cfg(feature = "client")]
use crate::options;
#[cfg(feature = "server")]
use std::time::{ Instant, Duration };
#[cfg(feature = "server")]
use crate::config;
#[derive(SchemaWrite, SchemaRead, PartialEq, Clone)]
pub enum MessageCode {
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, UploadLimit, InvalidUsage, InvalidFeature, KeepAlive, }
#[derive(SchemaWrite, SchemaRead, Clone)]
pub struct MessageColors {
pub username_color: Option<u8>, pub message_color: Option<u8>, }
#[derive(SchemaWrite, SchemaRead, Clone)]
pub struct FilePayload {
pub token: Option<[u8; 32]>, pub uid: u64, pub data: Option<Vec<u8>>, pub size: Option<u64>, pub filename: Option<String>, pub hash: Option<[u8; 32]>, }
#[derive(SchemaWrite, SchemaRead, Clone)]
pub struct MessagePacket {
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>, }
impl Default for MessagePacket {
fn default() -> Self
{
Self
{
text: None,
username: None,
id: None,
code: None,
colors: MessageColors
{
username_color: None,
message_color: None,
},
seq: 0,
file: None,
}
}
}
impl Default for FilePayload
{
fn default() -> Self
{
Self
{
token: None,
uid: 0,
data: None,
size: None,
filename: None,
hash: None,
}
}
}
fn obfuscate_data(mut data: Vec<u8>, obfuscation_key: [u8; 32]) -> Vec<u8> {
for (i, byte) in data.iter_mut().enumerate()
{
*byte ^= obfuscation_key[i % obfuscation_key.len()];
}
data
}
pub fn send (
stream: &mut TcpStream,
mut packet: MessagePacket,
keys: Option<&chat_consts::SharedKeys>,
#[cfg(feature = "server")] seq: Option<&mut usize>, )
{
#[cfg(feature = "client")]
{
packet.seq = options::get_seq() + 1;
options::set_seq(packet.seq);
}
#[cfg(feature = "server")]
{
match seq
{
Some(local_seq) =>
{
*local_seq += 1;
packet.seq = *local_seq;
},
None =>
{
let peer_addr = stream.peer_addr().ok();
if peer_addr.is_some() && let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr.unwrap())
{
if conn.is_authenticated()
{
packet.seq = conn.server_seq().unwrap() + 1;
*conn.server_seq_mut().unwrap() = packet.seq;
}
}
}
}
}
let packet_bytes = wincode::serialize(&packet).expect("Encoding packet failed");
let mut final_bytes = if let Some(keys) = keys
{
crypto::encrypt_packet::<{ consts::DEFAULT_GRID_WIDTH }, { consts::DEFAULT_GRID_HEIGHT }>(packet_bytes, keys)
} else
{
let key =
{
#[cfg(feature = "server")]
{
stream.peer_addr().ok()
.and_then(|addr| server::CONNECTIONS.get(&addr))
.and_then(|c| c.obfuscation_key().cloned())
.unwrap_or_else(|| [0u8; 32])
}
#[cfg(feature = "client")]
{
options::get_obfuscation_key()
}
};
obfuscate_data(packet_bytes, key) };
let packet_len = final_bytes.len();
let mut transmission_packet = Vec::with_capacity(4 + packet_len);
transmission_packet.extend_from_slice(&(packet_len as u32).to_be_bytes());
transmission_packet.append(&mut final_bytes);
let _ = stream.write_all(&transmission_packet);
stream.flush().expect("Flushing stream failed");
}
pub fn receive
(
streams: &mut Streams,
keys: Option<&chat_consts::SharedKeys>,
seq: Option<&mut usize> ) -> Option<MessagePacket>
{
#[cfg(feature = "server")]
let max_packet_size: usize;
#[cfg(feature = "server")]
let spam_protection = config::read_config::<bool>("spam_protection");
#[cfg(feature = "server")]
let peer_addr = streams.0.peer_addr().ok()?;
#[cfg(feature = "server")]
{
let authenticated = server::CONNECTIONS.get(&peer_addr)
.map(|conn| conn.is_authenticated())
.unwrap_or(false);
max_packet_size = if !spam_protection && authenticated
{
usize::MAX
} else {
config::read_config("max_packet_size")
};
}
let mut len_buf = [0u8; 4];
if streams.0.read_exact(&mut len_buf).is_err() {
#[cfg(feature = "server")]
server::remove_connection(&peer_addr, false, Some("length"));
return None;
}
let len = u32::from_be_bytes(len_buf) as usize;
#[cfg(feature = "server")]
if len > max_packet_size
{
server::remove_connection(&peer_addr, true, Some("length"));
return None;
}
let mut decoded_packet = vec![0u8; len];
if streams.0.read_exact(&mut decoded_packet).is_err() {
#[cfg(feature = "server")]
server::remove_connection(&peer_addr, false, Some("length"));
return None;
}
if let Some(keys) = keys
{
decoded_packet = match crypto::decrypt_packet::<{ consts::DEFAULT_GRID_WIDTH }, { consts::DEFAULT_GRID_HEIGHT }>(decoded_packet, keys)
{
Some(d) => d,
None => {
#[cfg(feature = "server")]
log::warn!("HMAC verification failed: {}", peer_addr);
return None;
}
}
} else
{
let key =
{
#[cfg(feature = "server")]
{
server::CONNECTIONS.get(&peer_addr)
.and_then(|c| c.obfuscation_key().cloned())
.unwrap_or_else(|| [0u8; 32])
}
#[cfg(feature = "client")]
{
options::get_obfuscation_key()
}
};
decoded_packet = obfuscate_data(decoded_packet, key); }
match wincode::deserialize::<MessagePacket>(&decoded_packet)
{
Ok(packet) =>
{
#[cfg(feature = "server")]
{
if let Some(seq) = seq
{
if packet.seq > *seq {
*seq = packet.seq;
} else { return None; }
} else if let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr)
{
let mut spam_warning = false;
let mut shared_key = None;
let mut disconnect = false;
let mut grace = true;
if packet.code != Some(MessageCode::KeepAlive) &&
packet.code != Some(MessageCode::KeyExchange) &&
packet.file.is_none()
{
if let Some(text) = &packet.text && conn.is_authenticated()
{
disconnect = text.len() > config::read_config("max_message_length");
}
if !disconnect && config::read_config("spam_protection") && conn.is_authenticated() &&
Instant::now().duration_since(*conn.last_activity()) <
Duration::from_millis(config::read_config::<u64>("min_message_delay"))
{
*conn.spam_violations_mut().unwrap() += 1;
spam_warning = true;
shared_key = conn.keys().cloned();
disconnect = *conn.spam_violations().unwrap() > config::read_config::<usize>("max_message_delay_violations");
}
*conn.last_activity_mut() = Instant::now(); }
if packet.seq > *conn.seq() {
*conn.seq_mut() = packet.seq;
} else
{
grace = false;
disconnect = true;
}
drop(conn);
if spam_warning
{
server::send_code(&mut streams.1.lock().unwrap(), None, MessageCode::SpamWarning, shared_key.as_ref());
}
if disconnect
{
server::remove_connection(&peer_addr, grace, Some(if !grace { "SEQ" } else { "SPAM" }));
return None;
}
}
}
#[cfg(feature = "client")] {
let used_seq = match seq
{
Some(ref s) => **s,
None => options::get_server_seq(),
};
if packet.seq > used_seq || used_seq == 0 || packet.code == Some(MessageCode::Disconnect) {
if let Some(seq) = seq
{
*seq = packet.seq;
} else
{
options::set_server_seq(packet.seq);
}
} else {
return None;
}
}
return Some(packet);
},
Err(_) =>
{
#[cfg(feature = "server")]
server::remove_connection(&peer_addr, false, Some("packet"));
return None;
}
}
}
pub fn send_file (
path: PathBuf,
mut stream: TcpStream,
uid: u64,
code: MessageCode,
keys: Option<&chat_consts::SharedKeys>,
)
{
let mut file = File::open(path).expect("Cannot open file for upload");
let mut buffer = vec![0; chat_consts::UPLOAD_CHUNK_SIZE];
#[cfg(feature = "server")]
let mut seq = 0usize;
loop
{
match file.read(&mut buffer)
{
Ok(0) => break, Ok(bytes) =>
{
send(&mut stream, MessagePacket
{
code: Some(code.clone()),
file: Some(FilePayload
{
uid,
data: Some(buffer[..bytes].to_vec()),
..Default::default()
}),
..Default::default()
}, keys, #[cfg(feature = "server")] Some(&mut seq));
},
Err(_) => {}, }
}
}