Changed whitelist system
This commit is contained in:
parent
574f9fa567
commit
2e067be5af
|
@ -15,7 +15,7 @@ use crate::listener;
|
|||
|
||||
pub async fn handle_client(
|
||||
client_socket: TcpStream,
|
||||
proxy_info: listener::ProxyInfo,
|
||||
mut proxy_info: listener::ProxyInfo,
|
||||
) {
|
||||
println!("Accepting Connection");
|
||||
let backend_addr = proxy_info.formatted_backend_address();
|
||||
|
@ -64,7 +64,7 @@ pub async fn handle_client(
|
|||
match server_conn {
|
||||
Some(mut server_conn) => {
|
||||
if login_handle::respond_login(
|
||||
&proxy_info,
|
||||
&mut proxy_info,
|
||||
&mut client_conn,
|
||||
&mut server_conn,
|
||||
).await.expect(
|
||||
|
|
|
@ -4,6 +4,8 @@ use tokio::net::TcpListener;
|
|||
use std::error::Error;
|
||||
use rsa::RsaPrivateKey;
|
||||
|
||||
use crate::whitelist::Whitelist;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum OnlineStatus {
|
||||
Online,
|
||||
|
@ -25,6 +27,7 @@ pub struct ProxyInfo {
|
|||
pub private_key: RsaPrivateKey,
|
||||
pub online_status: OnlineStatus,
|
||||
pub authentication_method: AuthenticationMethod,
|
||||
pub whitelist: Whitelist,
|
||||
}
|
||||
|
||||
impl ProxyInfo {
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
// Yeahbut December 2023
|
||||
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use serde_json::Value;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use purple_cello_mc_protocol::{
|
||||
mc_types::{self, Result, Packet, ProtocolConnection},
|
||||
|
@ -16,156 +10,10 @@ use purple_cello_mc_protocol::{
|
|||
use purple_cello_mojang_api::multiplayer_auth;
|
||||
|
||||
use crate::listener;
|
||||
|
||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
||||
|
||||
struct CachedWhitelist {
|
||||
whitelist_data: Value,
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Player {
|
||||
name: String,
|
||||
player_uuid: Option<u128>,
|
||||
}
|
||||
|
||||
enum PlayerAllowed {
|
||||
True(Player),
|
||||
False(String),
|
||||
}
|
||||
|
||||
fn load_whitelist() -> Value {
|
||||
let file_path = "./whitelist.json";
|
||||
|
||||
let data = match fs::read_to_string(file_path) {
|
||||
Ok(data) => data,
|
||||
Err(_) => return Value::Null,
|
||||
};
|
||||
|
||||
let whitelist_data: Value = match serde_json::from_str(&data) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Value::Null,
|
||||
};
|
||||
|
||||
whitelist_data
|
||||
}
|
||||
|
||||
fn get_whitelist() -> Vec<Player> {
|
||||
lazy_static! {
|
||||
static ref WHITELIST_CACHE: Arc<Mutex<Option<CachedWhitelist>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
}
|
||||
|
||||
let mut cache = WHITELIST_CACHE.lock().unwrap();
|
||||
|
||||
if let Some(cached_whitelist) = cache.as_ref() {
|
||||
if cached_whitelist.timestamp.elapsed() >= EXPIRATION_DURATION {
|
||||
println!("Refreshing whitelist cache");
|
||||
*cache = Some(CachedWhitelist {
|
||||
whitelist_data: load_whitelist(),
|
||||
timestamp: Instant::now(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
*cache = Some(CachedWhitelist {
|
||||
whitelist_data: load_whitelist(),
|
||||
timestamp: Instant::now(),
|
||||
});
|
||||
}
|
||||
|
||||
let whitelist_data = cache.as_ref().unwrap().whitelist_data.clone();
|
||||
|
||||
std::mem::drop(cache);
|
||||
|
||||
if whitelist_data == Value::Null {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let whitelist_array = match whitelist_data.as_array() {
|
||||
Some(whitelist) => whitelist,
|
||||
None => { return Vec::new(); }
|
||||
};
|
||||
|
||||
let mut whitelist: Vec<Player> = Vec::new();
|
||||
|
||||
for whitelisted_player in whitelist_array {
|
||||
let player_map = match whitelisted_player.as_object() {
|
||||
Some(whitelist) => whitelist,
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
let name = match player_map.get("name") {
|
||||
Some(name) => {
|
||||
match name.as_str() {
|
||||
Some(name) => name,
|
||||
None => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
let player_uuid = match player_map.get("uuid") {
|
||||
Some(uuid) => {
|
||||
match uuid.as_str() {
|
||||
Some(uuid) => {
|
||||
match u128::from_str_radix(uuid, 16) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
whitelist.push(Player {
|
||||
name: name.to_string(),
|
||||
player_uuid: Some(player_uuid),
|
||||
})
|
||||
}
|
||||
|
||||
whitelist
|
||||
}
|
||||
|
||||
fn check_player_whitelist(player: Player) -> PlayerAllowed {
|
||||
|
||||
if player.player_uuid.is_none() {
|
||||
return PlayerAllowed::False("Invalid UUID".to_string());
|
||||
}
|
||||
|
||||
let whitelist = get_whitelist();
|
||||
|
||||
let mut invalid_uuid = false;
|
||||
let mut invalid_username = false;
|
||||
|
||||
for wl_player in whitelist {
|
||||
if wl_player == player {
|
||||
return PlayerAllowed::True(player);
|
||||
} else if wl_player.name == player.name {
|
||||
invalid_uuid = true;
|
||||
} else if wl_player.player_uuid == player.player_uuid {
|
||||
invalid_username = true;
|
||||
}
|
||||
}
|
||||
|
||||
if invalid_uuid {
|
||||
PlayerAllowed::False("Invalid UUID".to_string())
|
||||
} else if invalid_username {
|
||||
PlayerAllowed::False(
|
||||
"Invalid Username!\nPlease contact the admins to update your \
|
||||
username:\npurplecelloserver@gmail.com".to_string()
|
||||
)
|
||||
} else {
|
||||
PlayerAllowed::False("Not whitelisted on this server.\n\
|
||||
Please direct whitelist requests to the admins:\n\
|
||||
purplecelloserver@gmail.com".to_string())
|
||||
}
|
||||
}
|
||||
use crate::whitelist::{Player, PlayerAllowed};
|
||||
|
||||
async fn check_player(
|
||||
proxy_info: &listener::ProxyInfo,
|
||||
proxy_info: &mut listener::ProxyInfo,
|
||||
player: Player,
|
||||
client_conn: &mut ProtocolConnection<'_>,
|
||||
) -> Result<PlayerAllowed> {
|
||||
|
@ -183,22 +31,24 @@ async fn check_player(
|
|||
listener::AuthenticationMethod::Mojang => {
|
||||
match multiplayer_auth::joined(
|
||||
&player.name, &server_id, None).await {
|
||||
Ok(_) => Ok(check_player_whitelist(player)),
|
||||
Ok(_) => Ok(proxy_info.whitelist
|
||||
.check_player_whitelist(player)
|
||||
),
|
||||
Err(_) => Ok(PlayerAllowed::False(
|
||||
"Mojang Authentication Failed".to_string()
|
||||
)),
|
||||
}},
|
||||
listener::AuthenticationMethod::None =>
|
||||
Ok(check_player_whitelist(player))
|
||||
Ok(proxy_info.whitelist.check_player_whitelist(player))
|
||||
}
|
||||
},
|
||||
listener::OnlineStatus::Offline =>
|
||||
Ok(check_player_whitelist(player)),
|
||||
Ok(proxy_info.whitelist.check_player_whitelist(player)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn respond_login(
|
||||
proxy_info: &listener::ProxyInfo,
|
||||
proxy_info: &mut listener::ProxyInfo,
|
||||
client_conn: &mut ProtocolConnection<'_>,
|
||||
server_conn: &mut ProtocolConnection<'_>,
|
||||
) -> Result<bool> {
|
||||
|
@ -225,7 +75,7 @@ pub async fn respond_login(
|
|||
}
|
||||
|
||||
async fn login_to_proxy(
|
||||
proxy_info: &listener::ProxyInfo,
|
||||
proxy_info: &mut listener::ProxyInfo,
|
||||
client_conn: &mut ProtocolConnection<'_>,
|
||||
) -> Result<PlayerAllowed> {
|
||||
println!("Logging into proxy");
|
||||
|
@ -236,6 +86,7 @@ async fn login_to_proxy(
|
|||
let player: Player = Player {
|
||||
name: start_packet.name,
|
||||
player_uuid: start_packet.player_uuid,
|
||||
active: true,
|
||||
};
|
||||
|
||||
check_player(proxy_info, player, client_conn).await
|
||||
|
|
|
@ -8,6 +8,7 @@ mod status_handle;
|
|||
mod login_handle;
|
||||
mod client;
|
||||
mod listener;
|
||||
mod whitelist;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
@ -20,6 +21,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
private_key: private_key.clone(),
|
||||
online_status: listener::OnlineStatus::Offline,
|
||||
authentication_method: listener::AuthenticationMethod::None,
|
||||
whitelist: whitelist::Whitelist::WhitelistOpen(
|
||||
whitelist::WhitelistOpen{}),
|
||||
};
|
||||
let online_info = listener::ProxyInfo{
|
||||
proxy_addr: "127.0.0.1".to_string(),
|
||||
|
@ -29,6 +32,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
private_key: private_key.clone(),
|
||||
online_status: listener::OnlineStatus::Online,
|
||||
authentication_method: listener::AuthenticationMethod::Mojang,
|
||||
whitelist: whitelist::Whitelist::WhitelistFile(
|
||||
whitelist::WhitelistFile::new("./whitelist.json".to_string())),
|
||||
};
|
||||
|
||||
let listener_offline: listener::TcpListenerWrapper =
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
// Yeahbut June 2024
|
||||
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(10);
|
||||
// const EXPIRATION_DURATION: Duration = Duration::from_secs(60);
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
pub player_uuid: Option<u128>,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
pub enum PlayerAllowed {
|
||||
True(Player),
|
||||
False(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Whitelist {
|
||||
WhitelistOpen(WhitelistOpen),
|
||||
WhitelistFile(WhitelistFile),
|
||||
}
|
||||
|
||||
impl Whitelist {
|
||||
pub fn check_player_whitelist(&mut self ,player: Player) -> PlayerAllowed {
|
||||
match self {
|
||||
Whitelist::WhitelistOpen(wl) => wl.check_player_whitelist(player),
|
||||
Whitelist::WhitelistFile(wl) => wl.check_player_whitelist(player),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WhitelistOpen {}
|
||||
|
||||
impl WhitelistOpen {
|
||||
pub fn check_player_whitelist(&mut self ,player: Player) -> PlayerAllowed {
|
||||
PlayerAllowed::True(player)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WhitelistFile {
|
||||
file_path: String,
|
||||
whitelist_data: Value,
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
impl WhitelistFile {
|
||||
pub fn new(file_path: String) -> Self {
|
||||
Self {
|
||||
file_path,
|
||||
whitelist_data: Value::Null,
|
||||
timestamp: Instant::now() - EXPIRATION_DURATION,
|
||||
}
|
||||
}
|
||||
|
||||
fn load(&mut self) {
|
||||
let data = match fs::read_to_string(&self.file_path) {
|
||||
Ok(data) => data,
|
||||
Err(_) => "".to_string(),
|
||||
};
|
||||
|
||||
self.whitelist_data = match serde_json::from_str(&data) {
|
||||
Ok(value) => value,
|
||||
Err(_) => Value::Null,
|
||||
};
|
||||
|
||||
self.timestamp = Instant::now();
|
||||
}
|
||||
|
||||
fn get_whitelist(&mut self) -> Vec<Player> {
|
||||
if self.timestamp.elapsed() >= EXPIRATION_DURATION {
|
||||
println!("Refreshing whitelist cache");
|
||||
self.load();
|
||||
}
|
||||
|
||||
if self.whitelist_data == Value::Null {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let whitelist_array = match self.whitelist_data.as_array() {
|
||||
Some(whitelist) => whitelist,
|
||||
None => { return Vec::new(); }
|
||||
};
|
||||
|
||||
let mut whitelist: Vec<Player> = Vec::new();
|
||||
|
||||
for whitelisted_player in whitelist_array {
|
||||
let player_map = match whitelisted_player.as_object() {
|
||||
Some(whitelist) => whitelist,
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
let name = match player_map.get("name") {
|
||||
Some(name) => {
|
||||
match name.as_str() {
|
||||
Some(name) => name,
|
||||
None => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
let player_uuid = match player_map.get("uuid") {
|
||||
Some(uuid) => {
|
||||
match uuid.as_str() {
|
||||
Some(uuid) => {
|
||||
match u128::from_str_radix(uuid, 16) {
|
||||
Ok(uuid) => uuid,
|
||||
Err(_) => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
}
|
||||
},
|
||||
None => { continue; }
|
||||
};
|
||||
|
||||
let active = match player_map.get("active") {
|
||||
Some(active) => {
|
||||
match active.as_bool() {
|
||||
Some(active) => active,
|
||||
None => { false }
|
||||
}
|
||||
},
|
||||
None => { false }
|
||||
};
|
||||
|
||||
whitelist.push(Player {
|
||||
name: name.to_string(),
|
||||
player_uuid: Some(player_uuid),
|
||||
active: active,
|
||||
});
|
||||
}
|
||||
|
||||
whitelist
|
||||
}
|
||||
|
||||
pub fn check_player_whitelist(&mut self ,player: Player) -> PlayerAllowed {
|
||||
|
||||
if player.player_uuid.is_none() {
|
||||
return PlayerAllowed::False(
|
||||
"Invalid UUID (UUID Missing)".to_string());
|
||||
}
|
||||
|
||||
let whitelist = self.get_whitelist();
|
||||
|
||||
let mut invalid_uuid = false;
|
||||
let mut invalid_username = false;
|
||||
let mut is_inactive = false;
|
||||
|
||||
for wl_player in whitelist {
|
||||
if wl_player.name == player.name &&
|
||||
wl_player.player_uuid == player.player_uuid {
|
||||
if wl_player.active {
|
||||
return PlayerAllowed::True(player);
|
||||
} else {
|
||||
is_inactive = true;
|
||||
}
|
||||
} else if wl_player.name == player.name &&
|
||||
wl_player.player_uuid != player.player_uuid {
|
||||
invalid_uuid = true;
|
||||
} else if wl_player.player_uuid == player.player_uuid &&
|
||||
wl_player.name != player.name {
|
||||
invalid_username = true;
|
||||
}
|
||||
}
|
||||
|
||||
if is_inactive {
|
||||
PlayerAllowed::False("Whitelist Status Inactive!\n\
|
||||
Please contact the admins to reactivate:\n\
|
||||
purplecelloserver@gmail.com".to_string()
|
||||
)
|
||||
} else if invalid_username {
|
||||
PlayerAllowed::False("Invalid Username!\n\
|
||||
Please contact the admins to update your username:\n\
|
||||
purplecelloserver@gmail.com".to_string()
|
||||
)
|
||||
} else if invalid_uuid {
|
||||
PlayerAllowed::False("Invalid UUID".to_string())
|
||||
} else {
|
||||
PlayerAllowed::False("Not whitelisted on this server.\n\
|
||||
Please direct whitelist requests to the admins:\n\
|
||||
purplecelloserver@gmail.com".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue