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