diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0196fd7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + } +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a32ee75..22d2fb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ version = "0.1.2+2.0" source = "git+https://github.com/MeexReay/bRAC.git?tag=0.1.2%2B2.0#52720c2748c3153f5ada996cc2b32366a9397549" dependencies = [ "clap", - "colored", + "colored 3.0.0", "lazy_static", "rand", "regex", @@ -275,12 +275,33 @@ dependencies = [ "cc", ] +[[package]] +name = "colog" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c426b7af8d5e0ad79de6713996632ce31f0d68ba84068fb0d654b396e519df0" +dependencies = [ + "colored 2.2.0", + "env_logger", + "log", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "colored" version = "3.0.0" @@ -343,6 +364,29 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -503,6 +547,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.33" @@ -620,6 +688,21 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -807,6 +890,8 @@ dependencies = [ "bRAC", "chrono", "clap", + "colog", + "log", "md-5", "rand", "rustls", @@ -910,9 +995,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" dependencies = [ "bytes", "data-encoding", diff --git a/Cargo.toml b/Cargo.toml index ba6a1a6..51e566c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,6 @@ md-5 = "0.10.6" rand = "0.9.0" clap = { version = "4.5.36", features = ["derive"] } rustls = "0.23.25" -tungstenite = "0.26.2" \ No newline at end of file +tungstenite = "0.27.0" +colog = "1.3.0" +log = "0.4.27" diff --git a/PROTOCOL.md b/PROTOCOL.md index 22fe3fa..d7cea5a 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -85,6 +85,8 @@ Server sends: Default port - 42667 +The same as RAC, but wrapped with TLS + # WRAC Protocol Default port - 52666 @@ -128,4 +130,6 @@ Server sends: # WRACS Protocol -Default port - 52667 \ No newline at end of file +Default port - 52667 + +Just the same WRAC protocol but wrapped with TLS, like RACS \ No newline at end of file diff --git a/src/ctx.rs b/src/ctx.rs new file mode 100644 index 0000000..99fbcb0 --- /dev/null +++ b/src/ctx.rs @@ -0,0 +1,273 @@ +use std::{ + collections::HashMap, + error::Error, + fs::OpenOptions, + io::{Cursor, Read, Write}, + net::IpAddr, + sync::{ + Arc, RwLock, + atomic::{AtomicU64, Ordering}, + }, + time::Duration, +}; + +use bRAC::{chat::format_message, util::sanitize_text}; +use chrono::{DateTime, Local, TimeZone}; +use log::info; +use md5::{Digest, Md5}; +use rand::{Rng, distr::Alphanumeric}; + +use crate::{Args, load_accounts, load_messages}; + +pub struct Context { + pub args: Arc, + pub messages_file: Option, + pub accounts_file: Option, + pub messages: RwLock>, + pub accounts: RwLock>, + pub messages_offset: AtomicU64, + pub notifications: RwLock>>, + pub timeouts: RwLock>, +} + +impl Context { + pub fn new( + args: Arc, + messages_file: Option, + accounts_file: Option, + ) -> Self { + Self { + args, + messages_file: messages_file.clone(), + accounts_file: accounts_file.clone(), + messages: RwLock::new(load_messages(messages_file.clone())), + accounts: RwLock::new(load_accounts(accounts_file.clone())), + timeouts: RwLock::new(HashMap::new()), + messages_offset: AtomicU64::default(), + notifications: RwLock::new(HashMap::new()), + } + } + + pub fn push_message(&self, msg: Vec) { + if let Some(messages_file) = self.messages_file.clone() { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(messages_file) + .expect("error messages file open"); + + file.write_all(&msg).expect("error messages file write"); + file.flush().expect("error messages file flush"); + } + + self.messages.write().unwrap().append(&mut msg.clone()); + + let content = self.messages.read().unwrap().clone(); + + if content.len() > self.args.messages_total_limit { + let offset = content.len() - self.args.messages_total_limit; + *self.messages.write().unwrap() = content[offset..].to_vec(); + self.messages_offset.store(offset as u64, Ordering::SeqCst); + } + } + + pub fn get_account_by_addr(&self, addr: &str) -> Option { + for acc in self.accounts.read().unwrap().iter().rev() { + if acc.addr() == addr { + return Some(acc.clone()); + } + } + None + } + + pub fn get_account(&self, name: &str) -> Option { + for acc in self.accounts.read().unwrap().iter() { + if acc.name() == name { + return Some(acc.clone()); + } + } + None + } + + pub fn push_account(&self, acc: Account) { + if let Some(accounts_file) = self.accounts_file.clone() { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(accounts_file) + .expect("error accounts file open"); + + file.write_all(&acc.to_bytes()) + .expect("error accounts file write"); + file.write_all(b"\n").expect("error accounts file write"); + file.flush().expect("error accounts file flush"); + } + + self.accounts.write().unwrap().push(acc); + } +} + +#[derive(Clone)] +pub struct Account { + name: String, + pass: Vec, + salt: String, + addr: String, + date: i64, +} + +pub fn password_hash(name: &str, pass: &str, salt: &str) -> Vec { + let mut hasher = Md5::new(); + hasher.update(format!("{name}{pass}{salt}").as_bytes()); + hasher.finalize().to_vec() +} + +pub fn password_salt() -> String { + rand::rng() + .sample_iter(&Alphanumeric) + .take(16) + .map(char::from) + .collect() +} + +impl Account { + pub fn new(name: String, password: String, addr: String, date: i64) -> Self { + let salt = password_salt(); + + Account { + pass: password_hash(&name, &password, &salt), + name: name.clone(), + salt: salt.clone(), + addr, + date, + } + } + + pub fn check_password(&self, password: &str) -> bool { + password_hash(&self.name, password, &self.salt) == self.pass + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn addr(&self) -> &str { + &self.addr + } + + pub fn date(&self) -> i64 { + self.date + } + + pub fn to_bytes(&self) -> Vec { + let mut data = Vec::new(); + data.append(&mut (self.name.len() as u32).to_le_bytes().to_vec()); + data.append(&mut (self.salt.len() as u32).to_le_bytes().to_vec()); + data.append(&mut (self.addr.len() as u32).to_le_bytes().to_vec()); + data.append(&mut (self.pass.len() as u32).to_le_bytes().to_vec()); + data.append(&mut self.name.as_bytes().to_vec()); + data.append(&mut self.salt.as_bytes().to_vec()); + data.append(&mut self.addr.as_bytes().to_vec()); + data.append(&mut self.pass.clone()); + data.append(&mut self.date.to_le_bytes().to_vec()); + data + } + + pub fn from_bytes(text: Vec) -> Self { + let mut text = Cursor::new(text); + + let mut name_len = [0; 4]; + text.read_exact(&mut name_len).unwrap(); + let name_len = u32::from_le_bytes(name_len) as usize; + + let mut salt_len = [0; 4]; + text.read_exact(&mut salt_len).unwrap(); + let salt_len = u32::from_le_bytes(salt_len) as usize; + + let mut addr_len = [0; 4]; + text.read_exact(&mut addr_len).unwrap(); + let addr_len = u32::from_le_bytes(addr_len) as usize; + + let mut pass_len = [0; 4]; + text.read_exact(&mut pass_len).unwrap(); + let pass_len = u32::from_le_bytes(pass_len) as usize; + + let mut name = vec![0; name_len]; + text.read_exact(&mut name).unwrap(); + let name = String::from_utf8_lossy(&name).to_string(); + + let mut salt = vec![0; salt_len]; + text.read_exact(&mut salt).unwrap(); + let salt = String::from_utf8_lossy(&salt).to_string(); + + let mut addr = vec![0; addr_len]; + text.read_exact(&mut addr).unwrap(); + let addr = String::from_utf8_lossy(&addr).to_string(); + + let mut pass = vec![0; pass_len]; + text.read_exact(&mut pass).unwrap(); + + let mut date = [0; 8]; + text.read_exact(&mut date).unwrap(); + let date = i64::from_le_bytes(date); + + Account { + name, + salt, + pass, + addr, + date, + } + } +} + +fn message_prefix(time_millis: i64, address: Option) -> String { + let datetime: DateTime = Local.timestamp_millis_opt(time_millis).unwrap(); + + format!( + "[{}]{} ", + datetime.format("%d.%m.%Y %H:%M"), + if let Some(addr) = address { + format!(" {{{addr}}}") + } else { + String::new() + } + ) +} + +pub fn add_message( + buf: &mut Vec, + context: Arc, + addr: Option, + sanitize: bool, +) -> Result<(), Box> { + let mut msg = Vec::new(); + + msg.append( + &mut message_prefix(Local::now().timestamp_millis(), addr.map(|o| o.to_string())) + .as_bytes() + .to_vec(), + ); + + if sanitize { + msg.append( + &mut sanitize_text(&String::from_utf8_lossy(&buf.clone())) + .as_bytes() + .to_vec(), + ); + } else { + msg.append(buf); + } + + if let Some(msg) = format_message(addr.is_some(), String::from_utf8_lossy(&msg).to_string()) { + info!("{}", msg); + } + + msg.push(b'\n'); + + context.push_message(msg); + + Ok(()) +} diff --git a/src/logic.rs b/src/logic.rs new file mode 100644 index 0000000..4a7bc8b --- /dev/null +++ b/src/logic.rs @@ -0,0 +1,135 @@ +use std::{ + error::Error, + net::SocketAddr, + sync::{Arc, atomic::Ordering}, +}; + +use chrono::Local; +use log::info; + +use crate::ctx::{Account, Context, add_message}; + +pub fn on_total_size(ctx: Arc, _: SocketAddr) -> Result> { + let messages_len = ctx.messages.read().unwrap().len() as u64; + let offset = ctx.messages_offset.load(Ordering::SeqCst); + + if let Some(splash) = &ctx.args.splash { + Ok(messages_len + splash.len() as u64 + offset) + } else { + Ok(messages_len + offset) + } +} + +pub fn on_total_data( + ctx: Arc, + _: SocketAddr, + _: Option, // sent_size +) -> Result, Box> { + let mut messages = ctx.messages.read().unwrap().clone(); + let offset = ctx.messages_offset.load(Ordering::SeqCst); + + let mut messages = if offset > 0 { + let mut buf = vec![0; offset as usize]; + buf.append(&mut messages); + buf + } else { + messages + }; + + if let Some(splash) = &ctx.args.splash { + messages.append(&mut splash.clone().as_bytes().to_vec()); + } + + Ok(messages) +} + +pub fn on_chunked_data( + ctx: Arc, + _: SocketAddr, + _: Option, // sent_size + client_has: u64, +) -> Result, Box> { + let messages = ctx.messages.read().unwrap().clone(); + let offset = ctx.messages_offset.load(Ordering::SeqCst); + let client_has = if let Some(splash) = &ctx.args.splash { + client_has - splash.len() as u64 + } else { + client_has + }; + + if client_has <= offset { + Ok(messages) + } else { + let client_has = (client_has - offset) as usize; + Ok(messages[client_has..].to_vec()) + } +} + +pub fn on_send_message( + ctx: Arc, + addr: SocketAddr, + message: Vec, +) -> Result<(), Box> { + if !ctx.args.auth_only { + add_message( + &mut message.clone(), + ctx.clone(), + Some(addr.ip()), + ctx.args.sanitize, + )?; + } + Ok(()) +} + +pub fn on_send_auth_message( + ctx: Arc, + _: SocketAddr, + name: &str, + password: &str, + text: &str, +) -> Result, Box> { + if let Some(acc) = ctx.get_account(name) { + if acc.check_password(password) { + add_message( + &mut text.as_bytes().to_vec(), + ctx.clone(), + None, + ctx.args.sanitize, + )?; + Ok(None) + } else { + Ok(Some(0x02)) + } + } else { + Ok(Some(0x01)) + } +} + +pub fn on_register_user( + ctx: Arc, + addr: SocketAddr, + name: &str, + password: &str, +) -> Result, Box> { + let addr = addr.ip().to_string(); + + let now: i64 = Local::now().timestamp_millis(); + + if ctx.get_account(name).is_some() + || (if let Some(acc) = ctx.get_account_by_addr(&addr) { + ((now - acc.date()) as usize) < 1000 * ctx.args.register_timeout + } else { + false + }) + { + return Ok(Some(0x01)); + } + + let account = Account::new(name.to_string(), password.to_string(), addr, now); + + info!("user registered: {name}"); + + ctx.push_account(account); + + Ok(None) +} diff --git a/src/main.rs b/src/main.rs index cf94a77..af46597 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,30 @@ use std::{ - collections::HashMap, error::Error, - fs::{self, OpenOptions}, - io::{Cursor, Read, Write}, - net::{IpAddr, SocketAddr, TcpListener}, - sync::{ - Arc, RwLock, - atomic::{AtomicUsize, Ordering}, - }, + fs, + io::{Read, Write}, + net::{SocketAddr, TcpListener}, + sync::Arc, thread, - time::Duration, }; -use bRAC::{chat::format_message, util::sanitize_text}; -use chrono::{DateTime, Local, TimeZone}; -use md5::{Digest, Md5}; -use rand::{Rng, distr::Alphanumeric}; +use log::{debug, info}; use clap::Parser; use rustls::{ ServerConfig, ServerConnection, StreamOwned, pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject}, }; -use tungstenite::{Bytes, Message, accept}; + +use crate::{ + ctx::{Account, Context}, + rac::accept_rac_stream, + wrac::accept_wrac_stream, +}; + +mod ctx; +mod logic; +mod rac; +mod wrac; fn load_accounts(accounts_file: Option) -> Vec { if let Some(accounts_file) = accounts_file.clone() { @@ -53,545 +55,6 @@ fn load_messages(messages_file: Option) -> Vec { } } -pub struct Context { - args: Arc, - messages_file: Option, - accounts_file: Option, - messages: RwLock>, - accounts: RwLock>, - messages_offset: AtomicUsize, - notifications: RwLock>>, - timeouts: RwLock>, -} - -impl Context { - fn new(args: Arc, messages_file: Option, accounts_file: Option) -> Self { - Self { - args, - messages_file: messages_file.clone(), - accounts_file: accounts_file.clone(), - messages: RwLock::new(load_messages(messages_file.clone())), - accounts: RwLock::new(load_accounts(accounts_file.clone())), - timeouts: RwLock::new(HashMap::new()), - messages_offset: AtomicUsize::default(), - notifications: RwLock::new(HashMap::new()), - } - } - - fn push_message(&self, msg: Vec) { - if let Some(messages_file) = self.messages_file.clone() { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(messages_file) - .expect("error messages file open"); - - file.write_all(&msg).expect("error messages file write"); - file.flush().expect("error messages file flush"); - } - - self.messages.write().unwrap().append(&mut msg.clone()); - - let content = self.messages.read().unwrap().clone(); - - if content.len() > self.args.messages_total_limit { - let offset = content.len() - self.args.messages_total_limit; - *self.messages.write().unwrap() = content[offset..].to_vec(); - self.messages_offset.store(offset, Ordering::SeqCst); - } - } - - fn get_account_by_addr(&self, addr: &str) -> Option { - for acc in self.accounts.read().unwrap().iter().rev() { - if acc.addr() == addr { - return Some(acc.clone()); - } - } - None - } - - fn get_account(&self, name: &str) -> Option { - for acc in self.accounts.read().unwrap().iter() { - if acc.name() == name { - return Some(acc.clone()); - } - } - None - } - - fn push_account(&self, acc: Account) { - if let Some(accounts_file) = self.accounts_file.clone() { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open(accounts_file) - .expect("error accounts file open"); - - file.write_all(&acc.to_bytes()) - .expect("error accounts file write"); - file.write_all(b"\n").expect("error accounts file write"); - file.flush().expect("error accounts file flush"); - } - - self.accounts.write().unwrap().push(acc); - } -} - -#[derive(Clone)] -pub struct Account { - name: String, - pass: Vec, - salt: String, - addr: String, - date: i64, -} - -fn password_hash(name: &str, pass: &str, salt: &str) -> Vec { - let mut hasher = Md5::new(); - hasher.update(format!("{name}{pass}{salt}").as_bytes()); - hasher.finalize().to_vec() -} - -fn password_salt() -> String { - rand::rng() - .sample_iter(&Alphanumeric) - .take(16) - .map(char::from) - .collect() -} - -impl Account { - pub fn new(name: String, password: String, addr: String, date: i64) -> Self { - let salt = password_salt(); - - Account { - pass: password_hash(&name, &password, &salt), - name: name.clone(), - salt: salt.clone(), - addr, - date, - } - } - - pub fn check_password(&self, password: &str) -> bool { - password_hash(&self.name, password, &self.salt) == self.pass - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn addr(&self) -> &str { - &self.addr - } - - pub fn date(&self) -> i64 { - self.date - } - - pub fn to_bytes(&self) -> Vec { - let mut data = Vec::new(); - data.append(&mut (self.name.len() as u32).to_le_bytes().to_vec()); - data.append(&mut (self.salt.len() as u32).to_le_bytes().to_vec()); - data.append(&mut (self.addr.len() as u32).to_le_bytes().to_vec()); - data.append(&mut (self.pass.len() as u32).to_le_bytes().to_vec()); - data.append(&mut self.name.as_bytes().to_vec()); - data.append(&mut self.salt.as_bytes().to_vec()); - data.append(&mut self.addr.as_bytes().to_vec()); - data.append(&mut self.pass.clone()); - data.append(&mut self.date.to_le_bytes().to_vec()); - data - } - - pub fn from_bytes(text: Vec) -> Self { - let mut text = Cursor::new(text); - - let mut name_len = [0; 4]; - text.read_exact(&mut name_len).unwrap(); - let name_len = u32::from_le_bytes(name_len) as usize; - - let mut salt_len = [0; 4]; - text.read_exact(&mut salt_len).unwrap(); - let salt_len = u32::from_le_bytes(salt_len) as usize; - - let mut addr_len = [0; 4]; - text.read_exact(&mut addr_len).unwrap(); - let addr_len = u32::from_le_bytes(addr_len) as usize; - - let mut pass_len = [0; 4]; - text.read_exact(&mut pass_len).unwrap(); - let pass_len = u32::from_le_bytes(pass_len) as usize; - - let mut name = vec![0; name_len]; - text.read_exact(&mut name).unwrap(); - let name = String::from_utf8_lossy(&name).to_string(); - - let mut salt = vec![0; salt_len]; - text.read_exact(&mut salt).unwrap(); - let salt = String::from_utf8_lossy(&salt).to_string(); - - let mut addr = vec![0; addr_len]; - text.read_exact(&mut addr).unwrap(); - let addr = String::from_utf8_lossy(&addr).to_string(); - - let mut pass = vec![0; pass_len]; - text.read_exact(&mut pass).unwrap(); - - let mut date = [0; 8]; - text.read_exact(&mut date).unwrap(); - let date = i64::from_le_bytes(date); - - Account { - name, - salt, - pass, - addr, - date, - } - } -} - -fn message_prefix(time_millis: i64, address: Option) -> String { - let datetime: DateTime = Local.timestamp_millis_opt(time_millis).unwrap(); - - format!( - "[{}]{} ", - datetime.format("%d.%m.%Y %H:%M"), - if let Some(addr) = address { - format!(" {{{addr}}}") - } else { - String::new() - } - ) -} - -fn add_message( - buf: &mut Vec, - context: Arc, - addr: Option, - sanitize: bool, -) -> Result<(), Box> { - let mut msg = Vec::new(); - - msg.append( - &mut message_prefix(Local::now().timestamp_millis(), addr.map(|o| o.to_string())) - .as_bytes() - .to_vec(), - ); - - if sanitize { - msg.append( - &mut sanitize_text(&String::from_utf8_lossy(&buf.clone())) - .as_bytes() - .to_vec(), - ); - } else { - msg.append(buf); - } - - if let Some(msg) = format_message(addr.is_some(), String::from_utf8_lossy(&msg).to_string()) { - println!("{}", msg); - } - - msg.push(b'\n'); - - context.push_message(msg); - - Ok(()) -} - -fn accept_wrac_stream( - stream: impl Read + Write, - addr: SocketAddr, - ctx: Arc, -) -> Result<(), Box> { - let mut websocket = match accept(stream) { - Ok(i) => i, - Err(e) => return Err(format!("accept websocket error: {}", e).into()), - }; - - while let Ok(msg) = websocket.read() { - if let Some(data) = match msg { - Message::Binary(o) => Some(o.to_vec()), - Message::Text(o) => Some(o.as_bytes().to_vec()), - Message::Close(_) => return Ok(()), - _ => None, - } { - let mut data = data; - let Some(id) = data.drain(..1).next() else { - return Ok(()); - }; - - if id == 0x00 { - let messages = ctx.messages.read().unwrap().clone(); - - let offset = ctx.messages_offset.load(Ordering::SeqCst); - - let mut messages = if offset > 0 { - let mut buf = vec![0; offset]; - buf.append(&mut messages.clone()); - buf - } else { - messages - }; - - if data.is_empty() { - if let Some(splash) = &ctx.args.splash { - websocket.write(Message::Binary(Bytes::from( - (messages.len() + splash.len() + offset) - .to_string() - .as_bytes() - .to_vec(), - )))?; - } else { - websocket.write(Message::Binary(Bytes::from( - (messages.len() + offset).to_string().as_bytes().to_vec(), - )))?; - } - websocket.flush()?; - } else { - let Some(id) = data.drain(..1).next() else { - return Ok(()); - }; - - if id == 0x01 { - if let Some(splash) = &ctx.args.splash { - messages.append(&mut splash.clone().as_bytes().to_vec()); - } - websocket.write(Message::Binary(Bytes::from(messages)))?; - websocket.flush()?; - } else if id == 0x02 { - let last_size: usize = String::from_utf8(data)?.parse()?; - if let Some(splash) = &ctx.args.splash { - websocket.write(Message::Binary(Bytes::from( - messages[(last_size - splash.len())..].to_vec(), - )))?; - } else { - websocket.write(Message::Binary(Bytes::from( - messages[last_size..].to_vec(), - )))?; - } - websocket.flush()?; - } - } - } else if id == 0x01 { - if !ctx.args.auth_only { - add_message(&mut data, ctx.clone(), Some(addr.ip()), ctx.args.sanitize)?; - } - } else if id == 0x02 { - let msg = String::from_utf8_lossy(&data).to_string(); - - let mut segments = msg.split("\n"); - - let Some(name) = segments.next() else { - return Ok(()); - }; - let Some(password) = segments.next() else { - return Ok(()); - }; - let Some(text) = segments.next() else { - return Ok(()); - }; - - if let Some(acc) = ctx.get_account(name) { - if acc.check_password(password) { - add_message( - &mut text.as_bytes().to_vec(), - ctx.clone(), - None, - ctx.args.sanitize, - )?; - } else { - websocket.write(Message::Binary(Bytes::from(vec![0x02])))?; - websocket.flush()?; - } - } else { - websocket.write(Message::Binary(Bytes::from(vec![0x01])))?; - websocket.flush()?; - } - } else if id == 0x03 { - let msg = String::from_utf8_lossy(&data).to_string(); - - let mut segments = msg.split("\n"); - - let Some(name) = segments.next() else { - return Ok(()); - }; - let Some(password) = segments.next() else { - return Ok(()); - }; - - let addr = addr.ip().to_string(); - - let now: i64 = Local::now().timestamp_millis(); - - if ctx.get_account(name).is_some() - || (if let Some(acc) = ctx.get_account_by_addr(&addr) { - ((now - acc.date()) as usize) < 1000 * ctx.args.register_timeout - } else { - false - }) - { - websocket.write(Message::Binary(Bytes::from(vec![0x01])))?; - websocket.flush()?; - continue; - } - - let account = Account::new(name.to_string(), password.to_string(), addr, now); - - println!("user registered: {name}"); - - ctx.push_account(account); - } - } - } - - Ok(()) -} - -fn accept_rac_stream( - mut stream: impl Read + Write, - addr: SocketAddr, - ctx: Arc, -) -> Result<(), Box> { - let mut buf = vec![0]; - stream.read_exact(&mut buf)?; - - if buf[0] == 0x00 { - let messages = ctx.messages.read().unwrap().clone(); - - let offset = ctx.messages_offset.load(Ordering::SeqCst); - - let mut messages = if offset > 0 { - let mut buf = vec![0; offset]; - buf.append(&mut messages.clone()); - buf - } else { - messages - }; - - if let Some(splash) = &ctx.args.splash { - stream.write_all( - (splash.len() + messages.len() + offset) - .to_string() - .as_bytes(), - )?; - - let mut id = vec![0]; - stream.read_exact(&mut id)?; - - if id[0] == 0x01 { - messages.append(&mut splash.clone().as_bytes().to_vec()); - stream.write_all(&messages)?; - } else if id[0] == 0x02 { - let mut buf = vec![0; 10]; - let size = stream.read(&mut buf)?; - buf.truncate(size); - - let len: usize = String::from_utf8(buf)?.parse()?; - stream.write_all(&messages[(len - splash.len())..])?; - } - } else { - stream.write_all((messages.len() + offset).to_string().as_bytes())?; - - let mut id = vec![0]; - stream.read_exact(&mut id)?; - - if id[0] == 0x01 { - stream.write_all(&messages)?; - } else if id[0] == 0x02 { - let mut buf = vec![0; 10]; - let size = stream.read(&mut buf)?; - buf.truncate(size); - - let len: usize = String::from_utf8(buf)?.parse()?; - stream.write_all(&messages[len..])?; - } - } - } else if buf[0] == 0x01 { - if !ctx.args.auth_only { - let mut buf = vec![0; 1024]; - let size = stream.read(&mut buf)?; - buf.truncate(size); - - add_message(&mut buf, ctx.clone(), Some(addr.ip()), ctx.args.sanitize)?; - } - } else if buf[0] == 0x02 { - let mut buf = vec![0; 8192]; - let size = stream.read(&mut buf)?; - buf.truncate(size); - - let msg = String::from_utf8_lossy(&buf).to_string(); - - let mut segments = msg.split("\n"); - - let Some(name) = segments.next() else { - return Ok(()); - }; - let Some(password) = segments.next() else { - return Ok(()); - }; - let Some(text) = segments.next() else { - return Ok(()); - }; - - if let Some(acc) = ctx.get_account(name) { - if acc.check_password(password) { - add_message( - &mut text.as_bytes().to_vec(), - ctx.clone(), - None, - ctx.args.sanitize, - )?; - } else { - stream.write_all(&[0x02])?; - } - } else { - stream.write_all(&[0x01])?; - } - } else if buf[0] == 0x03 { - let mut buf = vec![0; 1024]; - let size = stream.read(&mut buf)?; - buf.truncate(size); - - let msg = String::from_utf8_lossy(&buf).to_string(); - - let mut segments = msg.split("\n"); - - let Some(name) = segments.next() else { - return Ok(()); - }; - let Some(password) = segments.next() else { - return Ok(()); - }; - - let addr = addr.ip().to_string(); - - let now: i64 = Local::now().timestamp_millis(); - - if ctx.get_account(name).is_some() - || (if let Some(acc) = ctx.get_account_by_addr(&addr) { - ((now - acc.date()) as usize) < 1000 * ctx.args.register_timeout - } else { - false - }) - { - stream.write_all(&[0x01])?; - return Ok(()); - } - - let account = Account::new(name.to_string(), password.to_string(), addr, now); - - println!("user registered: {name}"); - - ctx.push_account(account); - } - - Ok(()) -} - fn accept_stream( stream: impl Read + Write, addr: SocketAddr, @@ -622,7 +85,7 @@ fn run_normal_listener(ctx: Arc) { match accept_stream(stream, addr, ctx) { Ok(_) => {} Err(e) => { - println!("{}", e) + debug!("{}", e) } } }); @@ -676,7 +139,7 @@ fn run_secure_listener(ctx: Arc) { match accept_stream(stream, addr, ctx) { Ok(_) => {} Err(e) => { - println!("{}", e) + debug!("{}", e) } } }); @@ -744,6 +207,8 @@ struct Args { } fn main() { + colog::init(); + let args = Arc::new(Args::parse()); let context = Arc::new(Context::new( @@ -752,7 +217,7 @@ fn main() { args.accounts_file.clone(), )); - println!("Server started on {}", &args.host); + info!("Server started on {}", &args.host); if args.enable_ssl { run_secure_listener(context); diff --git a/src/rac.rs b/src/rac.rs new file mode 100644 index 0000000..862449f --- /dev/null +++ b/src/rac.rs @@ -0,0 +1,92 @@ +use std::{ + error::Error, + io::{Read, Write}, + net::SocketAddr, + sync::Arc, +}; + +use crate::{ctx::Context, logic::*}; + +pub fn accept_rac_stream( + mut stream: impl Read + Write, + addr: SocketAddr, + ctx: Arc, +) -> Result<(), Box> { + let mut buf = vec![0]; + stream.read_exact(&mut buf)?; + + if buf[0] == 0x00 { + let total_size = on_total_size(ctx.clone(), addr.clone())?; + stream.write_all(total_size.to_string().as_bytes())?; + + let mut id = vec![0]; + stream.read_exact(&mut id)?; + + if id[0] == 0x01 { + stream.write_all(&on_total_data(ctx.clone(), addr.clone(), Some(total_size))?)?; + } else if id[0] == 0x02 { + let mut buf = vec![0; 10]; + let size = stream.read(&mut buf)?; + buf.truncate(size); + + let client_has: u64 = String::from_utf8(buf)?.parse()?; + stream.write_all(&on_chunked_data( + ctx.clone(), + addr.clone(), + Some(total_size), + client_has, + )?)?; + } + } else if buf[0] == 0x01 { + let mut buf = vec![0; 1024]; + let size = stream.read(&mut buf)?; + buf.truncate(size); + + on_send_message(ctx.clone(), addr.clone(), buf)?; + } else if buf[0] == 0x02 { + let mut buf = vec![0; 8192]; + let size = stream.read(&mut buf)?; + buf.truncate(size); + + let msg = String::from_utf8_lossy(&buf).to_string(); + + let mut segments = msg.split("\n"); + + let Some(name) = segments.next() else { + return Ok(()); + }; + let Some(password) = segments.next() else { + return Ok(()); + }; + let Some(text) = segments.next() else { + return Ok(()); + }; + + if let Some(resp_id) = + on_send_auth_message(ctx.clone(), addr.clone(), name, password, text)? + { + stream.write_all(&[resp_id])?; + } + } else if buf[0] == 0x03 { + let mut buf = vec![0; 1024]; + let size = stream.read(&mut buf)?; + buf.truncate(size); + + let msg = String::from_utf8_lossy(&buf).to_string(); + + let mut segments = msg.split("\n"); + + let Some(name) = segments.next() else { + return Ok(()); + }; + let Some(password) = segments.next() else { + return Ok(()); + }; + + if let Some(resp_id) = on_register_user(ctx.clone(), addr.clone(), name, password)? { + stream.write_all(&[resp_id])?; + } + } + + Ok(()) +} diff --git a/src/wrac.rs b/src/wrac.rs new file mode 100644 index 0000000..2a55c44 --- /dev/null +++ b/src/wrac.rs @@ -0,0 +1,113 @@ +use std::{ + error::Error, + io::{Read, Write}, + net::SocketAddr, + sync::Arc, +}; + +use tungstenite::{Bytes, Message, accept}; + +use crate::{ctx::Context, logic::*}; + +pub fn accept_wrac_stream( + stream: impl Read + Write, + addr: SocketAddr, + ctx: Arc, +) -> Result<(), Box> { + let mut websocket = match accept(stream) { + Ok(i) => i, + Err(e) => return Err(format!("accept websocket error: {}", e).into()), + }; + + let mut sent_size = None; + + while let Ok(msg) = websocket.read() { + if let Some(data) = match msg { + Message::Binary(o) => Some(o.to_vec()), + Message::Text(o) => Some(o.as_bytes().to_vec()), + Message::Close(_) => return Ok(()), + _ => None, + } { + let mut data = data; + let Some(id) = data.drain(..1).next() else { + return Ok(()); + }; + + if id == 0x00 { + if data.is_empty() { + let total_size = on_total_size(ctx.clone(), addr.clone())?; + sent_size = Some(total_size); + + websocket.write(Message::Binary(Bytes::from( + total_size.to_string().as_bytes().to_vec(), + )))?; + websocket.flush()?; + } else { + let Some(id) = data.drain(..1).next() else { + return Ok(()); + }; + + if id == 0x01 { + websocket.write(Message::Binary(Bytes::from(on_total_data( + ctx.clone(), + addr.clone(), + sent_size, + )?)))?; + websocket.flush()?; + } else if id == 0x02 { + let client_has = String::from_utf8(data)?.parse()?; + websocket.write(Message::Binary(Bytes::from(on_chunked_data( + ctx.clone(), + addr.clone(), + sent_size, + client_has, + )?)))?; + websocket.flush()?; + } + } + } else if id == 0x01 { + on_send_message(ctx.clone(), addr.clone(), data)?; + } else if id == 0x02 { + let msg = String::from_utf8_lossy(&data).to_string(); + + let mut segments = msg.split("\n"); + + let Some(name) = segments.next() else { + return Ok(()); + }; + let Some(password) = segments.next() else { + return Ok(()); + }; + let Some(text) = segments.next() else { + return Ok(()); + }; + + if let Some(resp_id) = + on_send_auth_message(ctx.clone(), addr.clone(), name, password, text)? + { + websocket.write(Message::Binary(Bytes::from(vec![resp_id])))?; + websocket.flush()?; + } + } else if id == 0x03 { + let msg = String::from_utf8_lossy(&data).to_string(); + + let mut segments = msg.split("\n"); + + let Some(name) = segments.next() else { + return Ok(()); + }; + let Some(password) = segments.next() else { + return Ok(()); + }; + + if let Some(resp_id) = on_register_user(ctx.clone(), addr.clone(), name, password)? + { + websocket.write(Message::Binary(Bytes::from(vec![resp_id])))?; + websocket.flush()?; + } + } + } + } + + Ok(()) +}