mirror of
https://github.com/MeexReay/sRAC.git
synced 2025-05-05 20:58:02 +03:00
merge commit
This commit is contained in:
commit
caa9df1646
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
/target
|
||||
/result
|
||||
/messages.txt
|
||||
/accounts.txt
|
||||
|
1216
Cargo.lock
generated
1216
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,3 +4,10 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.40"
|
||||
bRAC = { git = "https://github.com/MeexReay/bRAC.git", default-features = false, tag = "0.1.2+2.0" }
|
||||
md-5 = "0.10.6"
|
||||
rand = "0.9.0"
|
||||
clap = { version = "4.5.36", features = ["derive"] }
|
||||
rustls = "0.23.25"
|
||||
tungstenite = "0.26.2"
|
70
PROTOCOL.md
70
PROTOCOL.md
@ -13,9 +13,11 @@ Client sends:
|
||||
|
||||
## Reading messages
|
||||
|
||||
### Getting message length
|
||||
|
||||
Client sends:
|
||||
|
||||
- Byte `0x01`
|
||||
- Byte `0x00`
|
||||
|
||||
Server sends:
|
||||
|
||||
@ -23,6 +25,8 @@ Server sends:
|
||||
|
||||
### Normal reading
|
||||
|
||||
*Firstly, process getting message length packet*
|
||||
|
||||
Client sends:
|
||||
|
||||
- Byte `0x01`
|
||||
@ -33,6 +37,8 @@ Server sends:
|
||||
|
||||
### Chunked reading
|
||||
|
||||
*Firstly, process getting message length packet*
|
||||
|
||||
Client sends:
|
||||
|
||||
- Byte `0x02`
|
||||
@ -44,6 +50,23 @@ Server sends:
|
||||
|
||||
*for example: if you want to read last N bytes, last_size = data_size - N*
|
||||
|
||||
## Sending authorized messages
|
||||
|
||||
Client sends:
|
||||
|
||||
- Byte `0x02`
|
||||
- Username
|
||||
- `\n`
|
||||
- Password
|
||||
- `\n`
|
||||
- Message
|
||||
|
||||
Server sends:
|
||||
|
||||
- nothing if message was sent successfully
|
||||
- `0x01` if the user does not exists
|
||||
- `0x02` if the password is incorrect
|
||||
|
||||
## Registration users
|
||||
|
||||
Client sends:
|
||||
@ -55,17 +78,46 @@ Client sends:
|
||||
|
||||
Server sends:
|
||||
|
||||
- `0x00` if successful or `0x01` if the username is already taken
|
||||
- nothing if user was registered successfully
|
||||
- `0x01` if the username is already taken
|
||||
|
||||
*legacy servers send nothing on successful registration*
|
||||
# WRAC Protocol
|
||||
|
||||
## Sending authorized messages
|
||||
Default port - 42666
|
||||
|
||||
Uses websocket for connections, and sends binary data only
|
||||
|
||||
Totally inherits all packets except reading messages packets writed here
|
||||
|
||||
## Reading messages
|
||||
|
||||
### Normal reading
|
||||
|
||||
~~*Firstly, process getting message length packet*~~
|
||||
|
||||
This packet is independent from getting message length packet.
|
||||
|
||||
Client sends:
|
||||
|
||||
- **Byte `0x00`**
|
||||
- Byte `0x01`
|
||||
|
||||
Server sends:
|
||||
|
||||
- All messages
|
||||
|
||||
### Chunked reading
|
||||
|
||||
~~*Firstly, process getting message length packet*~~
|
||||
|
||||
This packet is independent from getting message length packet.
|
||||
|
||||
Client sends:
|
||||
|
||||
- **Byte `0x00`**
|
||||
- Byte `0x02`
|
||||
- Username
|
||||
- `\n`
|
||||
- Password
|
||||
- `\n`
|
||||
- Message
|
||||
- Size of messages you have in ASCII (last_size)
|
||||
|
||||
Server sends:
|
||||
|
||||
- All new messages
|
||||
|
29
README.md
29
README.md
@ -1,2 +1,29 @@
|
||||
# sRAC
|
||||
server for RAC
|
||||
simple server for RAC
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
git clone https://github.com/MeexReay/sRAC
|
||||
cd sRAC
|
||||
cargo run -- -H 127.0.0.1:42666
|
||||
```
|
||||
|
||||
### My server config
|
||||
|
||||
```bash
|
||||
cargo run -- \
|
||||
--host 127.0.0.1:42666 \
|
||||
--splash "please register (/register and /login commands in bRAC)" \
|
||||
--messages-file "messages.txt" \
|
||||
--accounts-file "accounts.txt" \
|
||||
--register-timeout 3600 \
|
||||
--sanitize \
|
||||
--auth-only
|
||||
```
|
||||
## Roadmap
|
||||
|
||||
- [ ] Notifications by ip
|
||||
- [ ] Server commands
|
||||
- [x] WRAC protocol
|
||||
- [x] RACS protocol
|
@ -1,4 +0,0 @@
|
||||
host: 127.0.0.1:42666
|
||||
|
||||
threadpool: 10
|
||||
timeout: 5 # in seconds
|
579
src/main.rs
579
src/main.rs
@ -1,3 +1,578 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use std::{error::Error, fs::{self, OpenOptions}, io::{Cursor, Read, Write}, net::{IpAddr, SocketAddr, TcpListener}, sync::{Arc, RwLock}, thread};
|
||||
|
||||
use bRAC::{chat::format_message, util::sanitize_text};
|
||||
use chrono::{DateTime, Local, TimeZone};
|
||||
use md5::{Digest, Md5};
|
||||
use rand::{distr::Alphanumeric, Rng};
|
||||
|
||||
use clap::Parser;
|
||||
use rustls::{pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer}, ServerConfig, ServerConnection, StreamOwned};
|
||||
use tungstenite::{accept, Bytes, Message, WebSocket};
|
||||
|
||||
|
||||
#[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>,
|
||||
messages: Arc<RwLock<Vec<u8>>>,
|
||||
addr: Option<IpAddr>,
|
||||
sanitize: bool,
|
||||
messages_file: Option<String>
|
||||
) -> 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');
|
||||
|
||||
if let Some(messages_file) = messages_file {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(messages_file)?;
|
||||
|
||||
file.write_all(&msg)?;
|
||||
file.flush()?;
|
||||
}
|
||||
|
||||
messages.write().unwrap().append(&mut msg.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accept_stream(
|
||||
args: Arc<Args>,
|
||||
mut stream: impl Read + Write,
|
||||
addr: SocketAddr,
|
||||
messages: Arc<RwLock<Vec<u8>>>,
|
||||
accounts: Arc<RwLock<Vec<Account>>>
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if args.enable_wrac {
|
||||
let mut websocket = accept(stream).unwrap();
|
||||
|
||||
while let Ok(msg) = websocket.read() {
|
||||
if let Some(data) = if msg.is_binary() {
|
||||
Some(msg.into_data().to_vec())
|
||||
} else if msg.is_text() {
|
||||
msg.into_text().ok().map(|o| o.as_bytes().to_vec())
|
||||
} else {
|
||||
None
|
||||
} {
|
||||
let mut data = data;
|
||||
let Some(id) = data.drain(..1).next() else { return Ok(()) };
|
||||
|
||||
if id == 0x00 {
|
||||
let mut messages = messages.read().unwrap().clone();
|
||||
|
||||
if data.is_empty() {
|
||||
if let Some(splash) = &args.splash {
|
||||
websocket.write(Message::Binary(Bytes::from((messages.len() + splash.len()).to_string().as_bytes().to_vec())))?;
|
||||
} else {
|
||||
websocket.write(Message::Binary(Bytes::from(messages.len().to_string().as_bytes().to_vec())))?;
|
||||
}
|
||||
} else {
|
||||
let Some(id) = data.drain(..1).next() else { return Ok(()) };
|
||||
|
||||
if id == 0x01 {
|
||||
if let Some(splash) = &args.splash {
|
||||
messages.append(&mut splash.clone().as_bytes().to_vec());
|
||||
}
|
||||
websocket.write(Message::Binary(Bytes::from(messages)))?;
|
||||
} else if id == 0x02 {
|
||||
let last_size: usize = String::from_utf8(data)?.parse()?;
|
||||
if let Some(splash) = &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())))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if id == 0x01 {
|
||||
if !args.auth_only {
|
||||
add_message(&mut data, messages.clone(), Some(addr.ip()), args.sanitize, args.messages_file.clone())?;
|
||||
}
|
||||
} 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(()) };
|
||||
|
||||
let mut sent = false;
|
||||
|
||||
for user in accounts.read().unwrap().iter() {
|
||||
if user.name() == name {
|
||||
if user.check_password(password) {
|
||||
add_message(&mut text.as_bytes().to_vec(), messages.clone(), None, args.sanitize, args.messages_file.clone())?;
|
||||
} else {
|
||||
websocket.write(Message::Binary(Bytes::from(vec![0x02])))?;
|
||||
}
|
||||
sent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !sent {
|
||||
websocket.write(Message::Binary(Bytes::from(vec![0x01])))?;
|
||||
}
|
||||
} 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();
|
||||
|
||||
let mut continue_send = false;
|
||||
|
||||
for user in accounts.read().unwrap().iter() {
|
||||
if user.name() == name {
|
||||
websocket.write(Message::Binary(Bytes::from(vec![0x01])))?;
|
||||
continue_send = true;
|
||||
break;
|
||||
}
|
||||
if user.addr() == addr && ((now - user.date()) as usize) < 1000 * args.register_timeout {
|
||||
websocket.write(Message::Binary(Bytes::from(vec![0x01])))?;
|
||||
continue_send = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if continue_send {
|
||||
continue;
|
||||
}
|
||||
|
||||
let account = Account::new(name.to_string(), password.to_string(), addr, now);
|
||||
|
||||
if let Some(accounts_file) = args.accounts_file.clone() {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(accounts_file)?;
|
||||
|
||||
file.write_all(&account.to_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
file.flush()?;
|
||||
}
|
||||
|
||||
accounts.write().unwrap().push(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut buf = vec![0];
|
||||
stream.read_exact(&mut buf)?;
|
||||
|
||||
if buf[0] == 0x00 {
|
||||
let mut messages = messages.read().unwrap().clone();
|
||||
|
||||
if let Some(splash) = &args.splash {
|
||||
stream.write_all((splash.len() + messages.len()).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().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 !args.auth_only {
|
||||
let mut buf = vec![0; 1024];
|
||||
let size = stream.read(&mut buf)?;
|
||||
buf.truncate(size);
|
||||
|
||||
add_message(&mut buf, messages.clone(), Some(addr.ip()), args.sanitize, args.messages_file.clone())?;
|
||||
}
|
||||
} 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(()) };
|
||||
|
||||
for user in accounts.read().unwrap().iter() {
|
||||
if user.name() == name {
|
||||
if user.check_password(password) {
|
||||
add_message(&mut text.as_bytes().to_vec(), messages.clone(), None, args.sanitize, args.messages_file.clone())?;
|
||||
} else {
|
||||
stream.write_all(&[0x02])?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for user in accounts.read().unwrap().iter() {
|
||||
if user.name() == name {
|
||||
stream.write_all(&[0x01])?;
|
||||
return Ok(());
|
||||
}
|
||||
if user.addr() == addr && ((now - user.date()) as usize) < 1000 * args.register_timeout {
|
||||
stream.write_all(&[0x01])?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let account = Account::new(name.to_string(), password.to_string(), addr, now);
|
||||
|
||||
if let Some(accounts_file) = args.accounts_file.clone() {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(accounts_file)?;
|
||||
|
||||
file.write_all(&account.to_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
file.flush()?;
|
||||
}
|
||||
|
||||
println!("user registered: {name}");
|
||||
|
||||
accounts.write().unwrap().push(account);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_normal_listener(messages: Arc<RwLock<Vec<u8>>>, accounts: Arc<RwLock<Vec<Account>>>, args: Arc<Args>) {
|
||||
let listener = TcpListener::bind(&args.host).expect("error trying bind to the provided addr");
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let Ok(stream) = stream else { continue };
|
||||
|
||||
let messages = messages.clone();
|
||||
let accounts = accounts.clone();
|
||||
let args = args.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let Ok(addr) = stream.peer_addr() else { return; };
|
||||
let _ = accept_stream(args, stream, addr, messages, accounts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn run_secure_listener(
|
||||
messages: Arc<RwLock<Vec<u8>>>,
|
||||
accounts: Arc<RwLock<Vec<Account>>>,
|
||||
args: Arc<Args>
|
||||
) {
|
||||
let listener = TcpListener::bind(&args.host).expect("error trying bind to the provided addr");
|
||||
|
||||
let server_config = Arc::new(ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(CertificateDer::pem_file_iter(
|
||||
args.ssl_cert.clone().expect("--ssl-cert is required"))
|
||||
.unwrap()
|
||||
.map(|cert| cert.unwrap())
|
||||
.collect(),
|
||||
PrivateKeyDer::from_pem_file(
|
||||
args.ssl_key.clone().expect("--ssl-key is required")).unwrap()
|
||||
).unwrap());
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let Ok(stream) = stream else { continue };
|
||||
|
||||
let messages = messages.clone();
|
||||
let accounts = accounts.clone();
|
||||
let args = args.clone();
|
||||
let server_config = server_config.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let Ok(addr) = stream.peer_addr() else { return; };
|
||||
|
||||
let Ok(connection) = ServerConnection::new(server_config) else { return };
|
||||
let mut stream = StreamOwned::new(connection, stream);
|
||||
|
||||
while stream.conn.is_handshaking() {
|
||||
let Ok(_) = stream.conn.complete_io(&mut stream.sock) else { return };
|
||||
}
|
||||
|
||||
let _ = accept_stream(args, stream, addr, messages, accounts);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version)]
|
||||
struct Args {
|
||||
/// Server host
|
||||
#[arg(short='H', long)]
|
||||
host: String,
|
||||
|
||||
/// Sanitize messages
|
||||
#[arg(short, long)]
|
||||
sanitize: bool,
|
||||
|
||||
/// Allow only authorized messages
|
||||
#[arg(short, long)]
|
||||
auth_only: bool,
|
||||
|
||||
/// Splash message
|
||||
#[arg(short='S', long)]
|
||||
splash: Option<String>,
|
||||
|
||||
/// Save messages to file
|
||||
#[arg(short='M', long)]
|
||||
messages_file: Option<String>,
|
||||
|
||||
/// Save accounts to file
|
||||
#[arg(short='A', long)]
|
||||
accounts_file: Option<String>,
|
||||
|
||||
/// Register timeout in seconds
|
||||
#[arg(short='r', long, default_value_t = 600)]
|
||||
register_timeout: usize,
|
||||
|
||||
/// Enable SSL (RACS)
|
||||
#[arg(short='l', long)]
|
||||
enable_ssl: bool,
|
||||
|
||||
/// Set ssl certificate path (x509)
|
||||
#[arg(long)]
|
||||
ssl_key: Option<String>,
|
||||
|
||||
/// Set ssl key path (x509)
|
||||
#[arg(long)]
|
||||
ssl_cert: Option<String>,
|
||||
|
||||
/// Enable WRAC
|
||||
#[arg(short='w', long)]
|
||||
enable_wrac: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Arc::new(Args::parse());
|
||||
|
||||
let messages = Arc::new(RwLock::new(
|
||||
if let Some(messages_file) = args.messages_file.clone() {
|
||||
if fs::exists(&messages_file).expect("error checking messages file") {
|
||||
fs::read(&messages_file).expect("error reading messages file")
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
));
|
||||
|
||||
let accounts = Arc::new(RwLock::new(
|
||||
if let Some(accounts_file) = args.accounts_file.clone() {
|
||||
if fs::exists(&accounts_file).expect("error checking accounts file") {
|
||||
fs::read(&accounts_file)
|
||||
.expect("error reading accounts file")
|
||||
.split(|o| *o == b'\n')
|
||||
.filter(|o| !o.is_empty())
|
||||
.map(|o| Account::from_bytes(o.to_vec()))
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
));
|
||||
|
||||
println!("Server started on {}", &args.host);
|
||||
|
||||
if args.enable_ssl {
|
||||
run_secure_listener(messages, accounts, args);
|
||||
} else {
|
||||
run_normal_listener(messages, accounts, args);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user