mirror of
https://github.com/MeexReay/sRAC.git
synced 2025-06-23 18:12:57 +03:00
refactorization
This commit is contained in:
parent
8dfb087e78
commit
2b4d1a4e27
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"[rust]": {
|
||||
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
91
Cargo.lock
generated
91
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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
273
src/ctx.rs
Normal 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
135
src/logic.rs
Normal 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)
|
||||
}
|
577
src/main.rs
577
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<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
92
src/rac.rs
Normal 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
113
src/wrac.rs
Normal 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(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user