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