why2_chat/network/
mod.rs

1/*
2This is part of WHY2
3Copyright (C) 2022-2026 Václav Šmejkal
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19//MODULES
20#[cfg(feature = "server")]
21pub mod server;
22
23#[cfg(feature = "client")]
24pub mod client;
25
26pub mod voice;
27
28use std::
29{
30    str::FromStr,
31    net::TcpStream,
32    mem::MaybeUninit,
33    io::{ Read, Write },
34    fmt::
35    {
36        self,
37        Display,
38        Formatter,
39    },
40};
41
42use wincode::
43{
44    SchemaWrite,
45    SchemaRead,
46    TypeMeta,
47    WriteResult,
48    ReadResult,
49    error::ReadError,
50    io::
51    {
52        Writer,
53        Reader,
54    },
55};
56
57use colored::Color;
58
59use crate::{ crypto, options };
60
61#[cfg(feature = "server")]
62use std::time::{ Instant, Duration };
63
64#[cfg(feature = "server")]
65use crate::config;
66
67//STRUCTS
68#[derive(SchemaWrite, SchemaRead, PartialEq, Clone)]
69pub enum MessageCode //CONTROL CODES
70{
71    KeyExchange,        //SERVER <> CLIENT | KEY EXCHANGE
72    Rekey,              //SERVER -> CLIENT | TRIGGER KEY EXCHANGE (USED FOR RE-KEYING)
73    Welcome,            //SERVER -> CLIENT | INFORMATIONS
74    Disconnect,         //SERVER <> CLIENT | QUIT COMMUNICATION
75    Username,           //SERVER -> CLIENT | PICK USERNAME
76    PasswordL,          //SERVER -> CLIENT | LOGIN
77    PasswordR,          //SERVER -> CLIENT | REGISTER
78    Accept,             //SERVER -> CLIENT | START CHATTING
79    Join,               //SERVER -> CLIENT | CLIENT JOIN MESSAGE
80    Leave,              //SERVER -> CLIENT | CLIENT LEAVE MESSAGE
81    List,               //CLIENT <> SERVER | PRINT CONNECTED USERS
82    PrivateMessage,     //CLIENT <> SERVER | SEND MESSAGE ONLY TO ONE CLIENT
83    PrivateMessageBack, //SERVER -> CLIENT | SEND MESSAGE BACK TO SENDER
84    SpamWarning,        //SERVER -> CLIENT | TELL CLIENT TO CALM TF DOWN
85    RegisterDisabled,   //SERVER -> CLIENT | REGISTRATION IS DISABLED
86    Version,            //SERVER <> CLIENT | ASK CLIENT FOR THEIR PKG VERSION
87    Channel,            //SERVER <> CLIENT | CHANNEL CHANGE
88    Voice,              //CLIENT <> SERVER | ESTABLISH VOICE CONNECTION
89    ChannelJoin,        //SERVER -> CLIENT | CLIENT JOINED VOICE CHANNEL
90    ChannelLeave,       //SERVER -> CLIENT | CLIENT LEFT VOICE CHANNEL
91    VoiceClients,       //SERVER -> CLIENT | TELL CLIENT ALL CONNECTED VOICE CLIENTS
92    InvalidUsage,       //SERVER -> CLIENT | INVALID PARAMETERS TO A COMMAND
93    InvalidFeature,     //SERVER -> CLIENT | CLIENT REQUESTED DISABLED FEATURE
94}
95
96#[derive(Clone)]
97pub struct SerColor(pub Color); //SERIALIZABLE Color
98
99#[derive(SchemaWrite, SchemaRead, Clone)]
100pub struct MessageColors //COLORS OF MESSAGE (ALL OF THE STRING VALUES WILL GET COVERTED TO colored::Color)
101{
102    pub username_color: Option<SerColor>, //COLOR OF SENDER
103    pub message_color: Option<SerColor>,  //COLOR OF MESSAGE
104}
105
106#[derive(SchemaWrite, SchemaRead, Clone)]
107pub struct MessagePacket //MESSAGE PACKET (WHAT IS BEING SENT)
108{
109    pub text: Option<String>,      //MESSAGE
110    pub username: Option<String>,  //USERNAME (SENT ONLY BY SERVER, AS SERVER DOESN'T ACCEPT USERNAMES FROM CLIENT)
111    pub id: Option<usize>,         //ID OF USER
112    pub code: Option<MessageCode>, //CONTROL CODE
113    pub colors: MessageColors,     //MESSAGE COLORS
114    pub seq: usize,                //SEQUENCE NUMBER
115}
116
117//IMPLEMENTATIONS
118impl Default for MessagePacket //DEFAULT
119{
120    fn default() -> Self
121    {
122        Self
123        {
124            text: None,
125            username: None,
126            id: None,
127            code: None,
128            colors: MessageColors
129            {
130                username_color: None,
131                message_color: None,
132            },
133            seq: 0,
134        }
135    }
136}
137
138impl Display for SerColor //PARSE SerColor TO STRING
139{
140    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
141    {
142        let color_str = match self.0
143        {
144            Color::Black => "black",
145            Color::Red => "red",
146            Color::Green => "green",
147            Color::Yellow => "yellow",
148            Color::Blue => "blue",
149            Color::Magenta => "magenta",
150            Color::Cyan => "cyan",
151            Color::BrightBlack => "bright black",
152            Color::BrightRed => "bright red",
153            Color::BrightGreen => "bright green",
154            Color::BrightYellow => "bright yellow",
155            Color::BrightBlue => "bright blue",
156            Color::BrightMagenta => "bright magenta",
157            Color::BrightCyan => "bright cyan",
158            Color::BrightWhite => "bright white",
159
160            _ => "white",
161        };
162
163        write!(f, "{color_str}")
164    }
165}
166
167impl FromStr for SerColor //PARSE STRING TO SerColor
168{
169    type Err = ReadError;
170
171    fn from_str(s: &str) -> Result<Self, Self::Err> {
172        Color::from_str(s)
173            .map(SerColor)
174            .map_err(|_| ReadError::Custom("Invalid color string"))
175    }
176}
177
178//SERIALIZE SerColor
179impl SchemaWrite for SerColor
180{
181    type Src = Self;
182    const TYPE_META: TypeMeta = TypeMeta::Dynamic;
183
184    fn size_of(src: &Self::Src) -> WriteResult<usize>
185    {
186        <String as SchemaWrite>::size_of(&src.to_string())
187    }
188
189    fn write(writer: &mut impl Writer, src: &Self::Src) -> WriteResult<()>
190    {
191        <String as SchemaWrite>::write(writer, &src.to_string())
192    }
193}
194
195//DESERIALIZE SerColor
196impl<'de> SchemaRead<'de> for SerColor
197{
198    type Dst = Self;
199    const TYPE_META: TypeMeta = TypeMeta::Dynamic;
200
201    fn read(reader: &mut impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()>
202    {
203        dst.write(<String as SchemaRead>::get(reader)?.parse::<SerColor>()?);
204        Ok(())
205    }
206}
207
208//FUNCTIONS
209//PRIVATE
210fn obfuscate_data(data: &[u8]) -> Vec<u8> //XOR BYTES (USED FOR OBFUSCATION)
211{
212    let mut obfuscated = data.to_vec();
213    for (i, byte) in obfuscated.iter_mut().enumerate()
214    {
215        //XOR EACH BYTE WITH OBFUSCATION KEY
216        *byte ^= options::OBFUSCATION_KEY[i % options::OBFUSCATION_KEY.len()];
217    }
218
219    obfuscated
220}
221
222//PUBLIC
223pub fn send(stream: &mut TcpStream, mut packet: MessagePacket, keys: Option<&options::SharedKeys>) //SEND packet TO stream
224{
225    //ADD SEQUENCE NUMBER TO packet (FROM CLIENT)
226    #[cfg(feature = "client")]
227    {
228        packet.seq = options::get_seq() + 1;
229        options::set_seq(packet.seq);
230    }
231
232    //ADD SEQUENCE NUMBER TO packet (FROM SERVER)
233    #[cfg(feature = "server")]
234    {
235        let peer_addr = stream.peer_addr().ok();
236        if peer_addr.is_some() && let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr.unwrap())
237        {
238            if conn.is_authenticated()
239            {
240                packet.seq = conn.server_seq().unwrap() + 1;
241                *conn.server_seq_mut().unwrap() = packet.seq;
242            }
243        }
244    }
245
246    //ENCODE THE PACKET STRUCT TO Vec<u8>
247    let packet_bytes = wincode::serialize(&packet).expect("Encoding packet failed");
248
249    let mut final_bytes = if let Some(keys) = keys
250    {
251        crypto::encrypt_packet(packet_bytes, keys)
252    } else
253    {
254        obfuscate_data(&packet_bytes) //NO ENCRYPTION, OBFUSCATE
255    };
256
257    //CONVERT ENCRYPTED OUTPUT TO BYTES ([LENGTH][DATA])
258    let packet_len = final_bytes.len();
259    let mut transmission_packet = Vec::with_capacity(4 + packet_len);
260    transmission_packet.extend_from_slice(&(packet_len as u32).to_be_bytes());
261    transmission_packet.append(&mut final_bytes);
262
263    //SEND
264    let _ = stream.write_all(&transmission_packet);
265    stream.flush().expect("Flushing stream failed");
266}
267
268pub fn receive(stream: &mut TcpStream, keys: Option<&options::SharedKeys>) -> Option<MessagePacket>
269{
270    //SERVER SIDE PACKET SIZE LIMIT
271    #[cfg(feature = "server")]
272    let max_packet_size: usize;
273
274    //SERVER SIDE SPAM PROTECTION
275    #[cfg(feature = "server")]
276    let spam_protection = config::server_config::<bool>("spam_protection");
277
278    #[cfg(feature = "server")]
279    let peer_addr = stream.peer_addr().ok()?; //GET CURRENT PEER ADDRESS
280
281    //SETUP LIMITS
282    #[cfg(feature = "server")]
283    {
284        //CHECK IF CLIENT IS AUTHENTICATED
285        let authenticated = server::CONNECTIONS.get(&peer_addr)
286            .map(|conn| conn.is_authenticated())
287            .unwrap_or(false);
288
289        //ALLOW BIG MESSAGES WHEN SPAM PROTECTION IS OFF AND CLIENT IS AUTHENTICATED
290        max_packet_size = if !spam_protection && authenticated
291        {
292            usize::MAX
293        } else //SET MAX PACKET SIZE IF SPAM PROTECTION IS ENABLED
294        {
295            config::server_config("max_packet_size")
296        };
297    }
298
299    //READ MESSAGE LENGTH
300    let mut len_buf = [0u8; 4];
301    if stream.read_exact(&mut len_buf).is_err() { return None; } //READ LENGTH
302    let len = u32::from_be_bytes(len_buf) as usize;
303
304    //CHECK PACKET SIZE
305    #[cfg(feature = "server")]
306    if len > max_packet_size
307    {
308         server::remove_connection(&peer_addr, true);
309         return None;
310    }
311
312    //READ REST OF PACKET
313    let mut decoded_packet = vec![0u8; len];
314    if stream.read_exact(&mut decoded_packet).is_err() { return None; } //READ
315
316    //DECRYPT
317    if let Some(keys) = keys
318    {
319        decoded_packet = match crypto::decrypt_packet(decoded_packet, keys)
320        {
321            Some(d) => d,
322            None => //INVALID MAC
323            {
324                //LOG IF ON SERVER
325                #[cfg(feature = "server")]
326                log::warn!("HMAC verification failed: {}", peer_addr);
327
328                return None;
329            }
330        }
331    } else
332    {
333        decoded_packet = obfuscate_data(&decoded_packet); //NO ENCRYPTION, REMOVE OBFUSCATION
334    }
335
336    //ACTIVITY TIMER ON SERVER
337    #[cfg(feature = "server")]
338    {
339        let mut spam_warning = false;
340        let mut shared_key = None;
341        let mut disconnect = false;
342
343        //FIND CONNECTION AND SET last_activity
344        if let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr)
345        {
346            //SPAM
347            if config::server_config("spam_protection") && conn.is_authenticated() &&
348                Instant::now().duration_since(*conn.last_activity()) <
349                    Duration::from_millis(config::server_config::<u64>("min_message_delay"))
350            {
351                //INCREMENT SPAM VIOLATIONS
352                *conn.spam_violations_mut().unwrap() += 1;
353
354                //WARN
355                spam_warning = true;
356                shared_key = conn.keys().cloned();
357
358                //CHECK FOR TOO MANY VIOLATIONS
359                disconnect = *conn.spam_violations().unwrap() > config::server_config::<usize>("max_message_delay_violations");
360            }
361
362            *conn.last_activity_mut() = Instant::now(); //RESET last_activity
363        }
364
365        //SEND WARNING CODE
366        if spam_warning
367        {
368            server::send_code(stream, None, MessageCode::SpamWarning, shared_key.as_ref());
369        }
370
371        //TOO MANY VIOLATIONS, BYE
372        if disconnect
373        {
374            server::remove_connection(&peer_addr, true);
375            return None;
376        }
377    }
378
379    //DESERIALIZE AND RETURN
380    match wincode::deserialize::<MessagePacket>(&decoded_packet)
381    {
382        Ok(packet) =>
383        {
384            //VERIFY SEQUENCE NUMBER
385            #[cfg(feature = "server")] //ON SERVER
386            {
387                if let Some(mut conn) = server::CONNECTIONS.get_mut(&peer_addr)
388                {
389                    if packet.seq > *conn.seq() //VALID SEQ
390                    {
391                        //SET SEQ TO CURRENT
392                        *conn.seq_mut() = packet.seq;
393                    } else
394                    {
395                        //INVALID SEQ
396                        drop(conn); //PREVENT DEADLOCK
397                        log::warn!("SEQ verification failed: {}", &peer_addr);
398                        server::remove_connection(&peer_addr, false);
399                    }
400                }
401            }
402
403            //VERIFY SEQUENCE NUMBER
404            #[cfg(feature = "client")] //ON CLIENT
405            {
406                if packet.seq > options::get_server_seq() || options::get_server_seq() == 0 || packet.code == Some(MessageCode::Disconnect) //VALID
407                {
408                    //SET SEQ
409                    options::set_server_seq(packet.seq);
410                } else //INVALID, DISCONNECT
411                {
412                    return None;
413                }
414            }
415
416            return Some(packet);
417        },
418
419        Err(_) =>
420        {
421            //FORCEFULLY DISCONNECT CLIENT ON INVALID PACKET
422            #[cfg(feature = "server")]
423            server::remove_connection(&peer_addr, false);
424
425            return None;
426        }
427    }
428}