config update

This commit is contained in:
MeexReay 2024-11-14 01:35:17 +03:00
parent 58fbcb46c6
commit 9dfc6da081
7 changed files with 225 additions and 122 deletions

28
Cargo.lock generated
View File

@ -59,6 +59,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "flate2"
version = "1.0.34"
@ -91,6 +100,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -125,6 +143,7 @@ version = "0.1.0"
dependencies = [
"ignore-result",
"log",
"random-string",
"rust_mc_proto",
"serde_yml",
"simplelog",
@ -185,6 +204,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "random-string"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f70fd13c3024ae3f17381bb5c4d409c6dc9ea6895c08fa2147aba305bea3c4af"
dependencies = [
"fastrand",
]
[[package]]
name = "rust_mc_proto"
version = "0.1.16"

View File

@ -9,8 +9,5 @@ rust_mc_proto = { git = "https://github.com/MeexReay/rust_mc_proto", features =
uuid = "1.11.0"
log = "0.4.22"
simplelog = "0.12.2"
# derivative = "2.2.0"
# no_deadlocks = "1.3.2"
# tokio = {version = "1.39.3", features = ["full"] }
# async-trait = "0.1.81"
ignore-result = "0.2.0"
ignore-result = "0.2.0"
random-string = "1.1.0"

View File

@ -6,5 +6,5 @@ Proxy for minecraft servers on rust
todo list: (✅ / ❌)
- ❌ add methods `connect_to_ip`, `connect_to_server`, `connect_to_stream`, `reconnect`
- ❌ make setting `no_pf_for_ip_connect` working
- ❌ make talk server
- ❌ create bukkit plugin for player formatting support and talking
- ❌ make messaging server
- ❌ create bukkit plugin for player formatting support and messaging

View File

@ -1,16 +1,34 @@
host: 127.0.0.1:25565 # host to bind meexprox
talk_host: 127.0.0.1:12346 # secret host to talk with meexprox (optional)
talk_secret: qwerty123456 # secret token for talk with meexprox (optional)
messaging: # messaging server (optional)
enabled: true
host: 127.0.0.1:12346 # host
secret: qwerty123456 # secret key
servers: # verified servers (name -> ip)
play: 127.0.0.1:12345
servers:
play: # server internal name
host: 127.0.0.1:12345 # server host
domains:
- _ # means that this server is default to connect players
- play.localhost
- mc.localhost
forwarding: # player forwarding
enabled: true
type: velocity
secret: "123456"
forced_hosts: # connect to server from connected hostname (name -> hostname) (optional)
play: play.localhost
default_forwarding: # player forwarding to use when you connecting by ip
enabled: false # disable player forwarding means that you dont need to transfer player's ip and other info to this server
# type: velocity
# secret: "123456"
default_server: play # default server to connect (optional)
incoming_forwarding: # player forwarding for incoming connections
enabled: false
# type: velocity
# secret: "123456"
player_forwarding: # how to transfer player ip to connected server (velocity:PASSWORD / bungeecord / bungeeguard:PASSWORD / none)
_: none # default player forwarding
play: velocity:123456 # specific player forwarding
# player forwarding types:
# - velocity (or "modern" in Velocity config) (secret is required)
# - bungeecord (or "legacy" in Velocity config) (secret is optional)
# - meexprox (best) (secret is required)
# - none (enabled: false)

View File

@ -1,33 +1,36 @@
use serde_yml::Value;
use serde_yml::{Mapping, Value};
use std::fs;
use std::path::Path;
use super::error::ProxyError;
#[derive(Clone, Debug)]
pub struct ServerInfo {
pub name: String,
pub host: String,
pub forced_host: Option<String>,
pub domains: Vec<String>,
pub player_forwarding: PlayerForwarding,
}
impl ServerInfo {
pub fn new(name: String, host: String, forced_host: Option<String>, player_forwarding: PlayerForwarding) -> ServerInfo {
pub fn new(
name: String,
host: String,
domains: Vec<String>,
player_forwarding: PlayerForwarding
) -> ServerInfo {
ServerInfo {
name,
host,
forced_host,
domains,
player_forwarding
}
}
pub fn from_host(host: String, config: ProxyConfig) -> ServerInfo {
pub fn from_host(host: String, player_forwarding: PlayerForwarding) -> ServerInfo {
ServerInfo {
name: host.clone(),
name: String::new(),
host,
forced_host: None,
player_forwarding: config.default_player_forwarding.clone()
domains: Vec::new(),
player_forwarding
}
}
}
@ -35,111 +38,136 @@ impl ServerInfo {
#[derive(Clone, Debug)]
pub enum PlayerForwarding {
Velocity(String),
Bungeecord,
Bungeeguard(String),
None,
Bungeecord(Option<String>),
Meexprox(String),
None
}
impl PlayerForwarding {
pub fn parse(name: &str) -> Result<PlayerForwarding, ProxyError> {
match name {
"bungeecord" => Ok(PlayerForwarding::Bungeecord),
"none" => Ok(PlayerForwarding::None),
pf => {
if pf.starts_with("bungeeguard:") {
Ok(PlayerForwarding::Bungeeguard(pf[9..].to_string()))
} else if pf.starts_with("velocity:") {
Ok(PlayerForwarding::Velocity(pf[9..].to_string()))
} else {
Err(ProxyError::ConfigParse)
pub fn from_data(data: Mapping) -> Option<PlayerForwarding> {
if data.len() == 0 { return None }
Some(if data.get("enabled")?.as_bool()? {
match data.get("type")?.as_str()? {
"velocity" => {
PlayerForwarding::Velocity(
data.get("secret")?
.as_str()?
.to_string()
)
}, "bungeecord" => {
PlayerForwarding::Bungeecord(
data.get("secret")
.map(|o| o.as_str())
.flatten()
.map(|o| o.to_string())
)
}, "meexprox" => {
PlayerForwarding::Meexprox(
data.get("secret")?
.as_str()?
.to_string()
)
}, _ => {
return None;
}
},
}
}
} else {
PlayerForwarding::None
})
}
}
#[derive(Clone)]
pub struct Messaging {
pub host: String,
pub secret: String
}
#[derive(Clone)]
pub struct ProxyConfig {
pub host: String,
pub servers: Vec<ServerInfo>,
pub default_server: Option<ServerInfo>,
pub talk_host: Option<String>,
pub talk_secret: Option<String>,
pub default_player_forwarding: PlayerForwarding,
pub messaging: Option<Messaging>,
pub default_forwarding: PlayerForwarding,
pub incoming_forwarding: PlayerForwarding
}
impl ProxyConfig {
pub fn new(
host: String,
servers: Vec<ServerInfo>,
default_server: Option<ServerInfo>,
talk_host: Option<String>,
talk_secret: Option<String>,
default_player_forwarding: PlayerForwarding
messaging: Option<Messaging>,
default_forwarding: PlayerForwarding,
incoming_forwarding: PlayerForwarding
) -> ProxyConfig {
ProxyConfig {
host,
servers,
default_server,
talk_host,
talk_secret,
default_player_forwarding
messaging,
default_forwarding,
incoming_forwarding
}
}
pub fn load_yml(data: String) -> Result<ProxyConfig, Box<dyn std::error::Error>> {
let data = serde_yml::from_str::<Value>(&data)?;
let data = data.as_mapping().ok_or(ProxyError::ConfigParse)?;
pub fn load_yml(data: String) -> Option<ProxyConfig> {
let data = serde_yml::from_str::<Value>(&data).ok()?;
let data = data.as_mapping()?;
let host = data.get("host").map(|o| o.as_str()).flatten().ok_or(ProxyError::ConfigParse)?.to_string();
let talk_host = data.get("talk_host").map(|o| o.as_str()).flatten().map(|o| o.to_string());
let talk_secret = data.get("talk_secret").map(|o| o.as_str()).flatten().map(|o| o.to_string());
let player_forwarding = data.get("player_forwarding").ok_or(ProxyError::ConfigParse)?.as_mapping().ok_or(ProxyError::ConfigParse)?.clone();
let default_player_forwarding = PlayerForwarding::parse(player_forwarding["_"].as_str().ok_or(ProxyError::ConfigParse)?)?;
let host = data.get("host")?.as_str()?.to_string();
let messaging = if let Some(map) = data.get("messaging") {
let map = map.as_mapping()?;
let mut servers = Vec::new();
if let Some(servers_map) = data
.get(&Value::String("servers".to_string()))
.and_then(Value::as_mapping)
{
for (name, addr) in servers_map {
if let (Value::String(name), Value::String(addr)) = (name, addr) {
servers.push(ServerInfo::new(name.clone(), addr.clone(), None,
player_forwarding.get(name).map(|o| o.as_str()).flatten()
.map(PlayerForwarding::parse).ok_or(ProxyError::ConfigParse)??));
}
if map.get("enabled")?.as_bool()? {
Some(Messaging {
host: map.get("host")?.as_str()?.to_string(),
secret: map.get("secret")?.as_str()?.to_string(),
})
} else {
None
}
}
} else {
None
};
if let Some(forced_hosts_map) = data
.get(&Value::String("forced_hosts".to_string()))
.and_then(Value::as_mapping)
{
for (name, host) in forced_hosts_map {
if let (Value::String(name), Value::String(host)) = (name, host) {
if let Some(server) = servers.iter_mut().find(|s| s.name == *name) {
server.forced_host = Some(host.clone());
}
}
}
}
let servers: Vec<ServerInfo> = data.get("servers")?.as_mapping()?
.iter()
.filter_map(|o| -> Option<ServerInfo> {
let map = o.1.as_mapping()?;
Some(ServerInfo::new(
o.0.as_str()?.to_string(),
map.get("host")?.as_str()?.to_string(),
map.get("domains")?.as_sequence()?
.iter()
.filter_map(|o| o.as_str())
.map(|o| o.to_string())
.collect(),
PlayerForwarding::from_data(
map.get("forwarding")?.as_mapping()?.clone()
)?
))
})
.collect();
let default_server = data.get("default_server")
.map(|o| o.as_str()).flatten()
.and_then(|ds| servers.iter().find(|s| s.name == ds).cloned());
let default_forwarding = PlayerForwarding::from_data(
data.get("default_forwarding")?.as_mapping()?.clone()
)?;
Ok(ProxyConfig::new(
let incoming_forwarding = PlayerForwarding::from_data(
data.get("incoming_forwarding")?.as_mapping()?.clone()
)?;
Some(ProxyConfig::new(
host,
servers,
default_server,
talk_host,
talk_secret,
default_player_forwarding,
messaging,
default_forwarding,
incoming_forwarding
))
}
pub fn load(path: impl AsRef<Path>) -> Result<ProxyConfig, Box<dyn std::error::Error>> {
Self::load_yml(fs::read_to_string(path)?)
pub fn load(path: impl AsRef<Path>) -> Option<ProxyConfig> {
Self::load_yml(fs::read_to_string(path).ok()?)
}
pub fn get_server_by_name(&self, name: &str) -> Option<ServerInfo> {
@ -151,14 +179,19 @@ impl ProxyConfig {
None
}
pub fn get_server_by_forced_host(&self, forced_host: &str) -> Option<ServerInfo> {
pub fn get_server_by_domain(&self, domain: &str) -> Option<ServerInfo> {
for server in &self.servers {
if let Some(server_forced_host) = &server.forced_host {
if server_forced_host == forced_host {
return Some(server.clone());
}
if server.domains.contains(&domain.to_string()) {
return Some(server.clone());
}
}
for server in &self.servers {
if server.domains.contains(&"_".to_string()) {
return Some(server.clone());
}
}
None
}
}

View File

@ -1,7 +1,7 @@
use std::{net::TcpStream, sync::{Arc, Mutex}, thread};
use std::{net::{SocketAddr, TcpStream}, sync::{Arc, Mutex}, thread};
use ignore_result::Ignore;
use rust_mc_proto::{DataBufferReader, MCConnTcp, Packet};
use rust_mc_proto::{DataBufferReader, DataBufferWriter, MCConnTcp, Packet, ProtocolError};
use uuid::Uuid;
use super::{config::{ProxyConfig, ServerInfo}, error::{AsProxyResult, ProxyError}};
@ -18,8 +18,45 @@ pub struct LoginInfo {
}
impl LoginInfo {
pub fn write(&self, config: &ProxyConfig, stream: &mut MCConnTcp) {
todo!() // TODO: write login packets sending
pub fn write(&self, _config: &ProxyConfig, stream: &mut MCConnTcp) -> Result<(), ProtocolError> {
stream.write_packet(&Packet::build(0x00, |p| {
p.write_u16_varint(self.protocol_version)?;
p.write_string(&self.server_address)?;
p.write_short(self.server_port as i16)?;
p.write_u8_varint(2)
})?)?;
stream.write_packet(&Packet::build(0x00, |p| {
p.write_string(&self.name)?;
p.write_uuid(&self.uuid)
})?)?;
loop {
let mut packet = stream.read_packet()?;
match packet.id() {
0x01 => {
stream.write_packet(&Packet::build(0x00, |p| {
p.write_usize_varint(self.shared_secret.as_ref().unwrap().len())?;
p.write_bytes(&self.shared_secret.as_ref().unwrap())?;
p.write_usize_varint(self.verify_token.as_ref().unwrap().len())?;
p.write_bytes(&self.verify_token.as_ref().unwrap())
})?)?;
}
0x02 => {
break;
}
0x03 => {
let compression = Some(packet.read_usize_varint()?);
stream.set_compression(compression);
}
_ => {}
}
}
stream.write_packet(&Packet::empty(0x03))?;
Ok(())
}
}
@ -30,7 +67,8 @@ pub struct Player {
pub name: String,
pub uuid: Uuid,
pub server: Option<ServerInfo>,
pub protocol_version: u16
pub protocol_version: u16,
pub addr: SocketAddr
}
impl Player {
@ -39,6 +77,7 @@ impl Player {
server_address: String,
server_port: u16,
server: ServerInfo,
addr: SocketAddr,
mut client_conn: MCConnTcp,
mut server_conn: MCConnTcp
) -> Result<Player, ProxyError> {
@ -52,6 +91,7 @@ impl Player {
server_conn.write_packet(&packet).as_proxy()?;
let mut player = Player {
addr,
client_conn: Arc::new(Mutex::new(client_conn)),
server_conn: Arc::new(Mutex::new(server_conn)),
login_info: None,

View File

@ -1,5 +1,4 @@
use log::{error, info};
// use no_deadlocks::Mutex;
use rust_mc_proto::{
read_packet, write_packet, DataBufferReader, DataBufferWriter, MCConnTcp, Packet
};
@ -64,8 +63,7 @@ impl MeexProx {
let next_state = handshake.read_u8_varint().as_proxy()?;
let server = self.config
.get_server_by_forced_host(&server_address)
.or(self.config.default_server.clone())
.get_server_by_domain(&server_address)
.ok_or(ProxyError::ConfigParse)?;
let mut server_conn = TcpStream::connect(&server.host).map_err(|_| ProxyError::ServerConnect)?;
@ -76,18 +74,6 @@ impl MeexProx {
handshake.write_unsigned_short(server_port)?;
handshake.write_u8_varint(next_state)?;
if let PlayerForwarding::Handshake = self.config.player_forwarding {
if let SocketAddr::V4(addr) = addr {
handshake.write_boolean(false)?; // is ipv6
handshake.write_unsigned_short(addr.port())?; // port
handshake.write_bytes(&addr.ip().octets())?; // octets
} else if let SocketAddr::V6(addr) = addr {
handshake.write_boolean(true)?;
handshake.write_unsigned_short(addr.port())?;
handshake.write_bytes(&addr.ip().octets())?;
}
}
Ok(())
}).as_proxy()?;
@ -107,6 +93,7 @@ impl MeexProx {
server_address,
server_port,
server,
addr,
client_conn,
server_conn
)?);