refactorization

This commit is contained in:
MeexReay 2025-06-17 01:47:45 +03:00
parent 8dfb087e78
commit 2b4d1a4e27
9 changed files with 736 additions and 561 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"editor.formatOnSave": true
}
}

91
Cargo.lock generated
View File

@ -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",

View File

@ -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"
tungstenite = "0.27.0"
colog = "1.3.0"
log = "0.4.27"

View File

@ -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
Default port - 52667
Just the same WRAC protocol but wrapped with TLS, like RACS

273
src/ctx.rs Normal file
View File

@ -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<Args>,
pub messages_file: Option<String>,
pub accounts_file: Option<String>,
pub messages: RwLock<Vec<u8>>,
pub accounts: RwLock<Vec<Account>>,
pub messages_offset: AtomicU64,
pub notifications: RwLock<HashMap<u32, Vec<u8>>>,
pub timeouts: RwLock<HashMap<u32, Duration>>,
}
impl Context {
pub fn new(
args: Arc<Args>,
messages_file: Option<String>,
accounts_file: Option<String>,
) -> 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<u8>) {
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<Account> {
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<Account> {
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<u8>,
salt: String,
addr: String,
date: i64,
}
pub fn password_hash(name: &str, pass: &str, salt: &str) -> Vec<u8> {
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<u8> {
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<u8>) -> 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>) -> String {
let datetime: DateTime<Local> = 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<u8>,
context: Arc<Context>,
addr: Option<IpAddr>,
sanitize: bool,
) -> Result<(), Box<dyn Error>> {
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(())
}

135
src/logic.rs Normal file
View File

@ -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<Context>, _: SocketAddr) -> Result<u64, Box<dyn Error>> {
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<Context>,
_: SocketAddr,
_: Option<u64>, // sent_size
) -> Result<Vec<u8>, Box<dyn Error>> {
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<Context>,
_: SocketAddr,
_: Option<u64>, // sent_size
client_has: u64,
) -> Result<Vec<u8>, Box<dyn Error>> {
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<Context>,
addr: SocketAddr,
message: Vec<u8>,
) -> Result<(), Box<dyn Error>> {
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<Context>,
_: SocketAddr,
name: &str,
password: &str,
text: &str,
) -> Result<Option<u8>, Box<dyn Error>> {
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<Context>,
addr: SocketAddr,
name: &str,
password: &str,
) -> Result<Option<u8>, Box<dyn Error>> {
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)
}

View File

@ -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<String>) -> Vec<Account> {
if let Some(accounts_file) = accounts_file.clone() {
@ -53,545 +55,6 @@ fn load_messages(messages_file: Option<String>) -> Vec<u8> {
}
}
pub struct Context {
args: Arc<Args>,
messages_file: Option<String>,
accounts_file: Option<String>,
messages: RwLock<Vec<u8>>,
accounts: RwLock<Vec<Account>>,
messages_offset: AtomicUsize,
notifications: RwLock<HashMap<u32, Vec<u8>>>,
timeouts: RwLock<HashMap<u32, Duration>>,
}
impl Context {
fn new(args: Arc<Args>, messages_file: Option<String>, accounts_file: Option<String>) -> 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<u8>) {
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<Account> {
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<Account> {
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<u8>,
salt: String,
addr: String,
date: i64,
}
fn password_hash(name: &str, pass: &str, salt: &str) -> Vec<u8> {
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<u8> {
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<u8>) -> 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>) -> String {
let datetime: DateTime<Local> = 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<u8>,
context: Arc<Context>,
addr: Option<IpAddr>,
sanitize: bool,
) -> Result<(), Box<dyn Error>> {
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<Context>,
) -> Result<(), Box<dyn Error>> {
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<Context>,
) -> Result<(), Box<dyn Error>> {
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<Context>) {
match accept_stream(stream, addr, ctx) {
Ok(_) => {}
Err(e) => {
println!("{}", e)
debug!("{}", e)
}
}
});
@ -676,7 +139,7 @@ fn run_secure_listener(ctx: Arc<Context>) {
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);

92
src/rac.rs Normal file
View File

@ -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<Context>,
) -> Result<(), Box<dyn Error>> {
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(())
}

113
src/wrac.rs Normal file
View File

@ -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<Context>,
) -> Result<(), Box<dyn Error>> {
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(())
}