Compare commits
No commits in common. "149a3acc1d8293c9ef3da30220ffdc097a006ee3" and "eaf4b8cb2bb64a9c658eab25c6c098c5dfb3bdc8" have entirely different histories.
149a3acc1d
...
eaf4b8cb2b
|
@ -8,11 +8,10 @@ license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
purple_cello_mc_protocol = { git = "https://github.com/PurpleCelloServer/purple_cello_mc_protocol.git", rev = "9ff2e95c66b1d773362936733273350e4bdd399a" }
|
purple_cello_mc_protocol = { git = "https://github.com/PurpleCelloServer/purple_cello_mc_protocol.git", rev = "b1a8cfdc91657566d68fb6f7dbc71bdee390cc9e" }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
base64 = "0.21.5"
|
base64 = "0.21.5"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
rsa = "0.6"
|
|
||||||
|
|
158
src/client.rs
158
src/client.rs
|
@ -1,158 +0,0 @@
|
||||||
// Yeahbut May 2024
|
|
||||||
|
|
||||||
use std::mem;
|
|
||||||
use tokio::net::{TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}};
|
|
||||||
|
|
||||||
use purple_cello_mc_protocol::{
|
|
||||||
mc_types::{self, Packet, ProtocolConnection},
|
|
||||||
handshake,
|
|
||||||
login,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::status_handle;
|
|
||||||
use crate::login_handle;
|
|
||||||
use crate::listener;
|
|
||||||
|
|
||||||
pub async fn handle_client(
|
|
||||||
client_socket: TcpStream,
|
|
||||||
proxy_info: listener::ProxyInfo,
|
|
||||||
) {
|
|
||||||
println!("Accepting Connection");
|
|
||||||
let backend_addr = proxy_info.formatted_backend_address();
|
|
||||||
|
|
||||||
let (mut client_reader, mut client_writer) = client_socket.into_split();
|
|
||||||
let mut client_conn = ProtocolConnection::new(
|
|
||||||
&mut client_reader,
|
|
||||||
&mut client_writer,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut backend_socket: (OwnedReadHalf, OwnedWriteHalf);
|
|
||||||
let mut server_conn: Option<ProtocolConnection<'_>> =
|
|
||||||
match TcpStream::connect(backend_addr).await {
|
|
||||||
Ok(backend_stream) => {
|
|
||||||
backend_socket = backend_stream.into_split();
|
|
||||||
Some(ProtocolConnection::new(
|
|
||||||
&mut backend_socket.0, &mut backend_socket.1))
|
|
||||||
},
|
|
||||||
Err(_) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer: [u8; 1] = [0; 1];
|
|
||||||
client_conn.stream_read.peek(&mut buffer)
|
|
||||||
.await.expect("Failed to peek at first byte from stream");
|
|
||||||
let packet_id: u8 = buffer[0];
|
|
||||||
|
|
||||||
if packet_id == 0xFE {
|
|
||||||
status_handle::respond_legacy_status(&mut client_conn)
|
|
||||||
.await.expect("Error handling legacy status request");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
let handshake_packet =
|
|
||||||
handshake::serverbound::Handshake::read(&mut client_conn)
|
|
||||||
.await.expect("Error reading handshake packet");
|
|
||||||
println!("Next state: {}", handshake_packet.next_state);
|
|
||||||
if handshake_packet.next_state == 1 {
|
|
||||||
println!("Receiving Status Request");
|
|
||||||
status_handle::respond_status(
|
|
||||||
proxy_info,
|
|
||||||
&mut client_conn,
|
|
||||||
&mut server_conn,
|
|
||||||
).await.expect("Error handling status request");
|
|
||||||
return;
|
|
||||||
} else if handshake_packet.next_state == 2 {
|
|
||||||
if handshake_packet.protocol_version == mc_types::VERSION_PROTOCOL {
|
|
||||||
match server_conn {
|
|
||||||
Some(mut server_conn) => {
|
|
||||||
if login_handle::respond_login(
|
|
||||||
&proxy_info,
|
|
||||||
&mut client_conn,
|
|
||||||
&mut server_conn,
|
|
||||||
).await.expect(
|
|
||||||
"Error logging into proxy or server"
|
|
||||||
) {
|
|
||||||
handle_play(
|
|
||||||
client_conn,
|
|
||||||
server_conn,
|
|
||||||
).await;
|
|
||||||
} else {
|
|
||||||
println!("Player blocked from server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
login::clientbound::Disconnect {
|
|
||||||
reason: "\"Server Error (Server is down or \
|
|
||||||
restarting)\nPlease contact the admins if the issue persists:\n\
|
|
||||||
purplecelloserver@gmail.com\""
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
.write(&mut client_conn)
|
|
||||||
.await
|
|
||||||
.expect("Error sending disconnect on: \
|
|
||||||
Failed to connect to the backend server");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if handshake_packet.protocol_version < mc_types::VERSION_PROTOCOL {
|
|
||||||
println!("Client on outdated version");
|
|
||||||
login::clientbound::Disconnect {
|
|
||||||
reason: format!(
|
|
||||||
"\"Client Error: Outdated Version (I'm on {})\"",
|
|
||||||
mc_types::VERSION_NAME,
|
|
||||||
).to_string()
|
|
||||||
}
|
|
||||||
.write(&mut client_conn).await.expect(
|
|
||||||
"Error sending disconnect on: Client on wrong version");
|
|
||||||
// if handshake_packet.protocol_version > mc_types::VERSION_PROTOCOL
|
|
||||||
} else {
|
|
||||||
println!("Client on future version");
|
|
||||||
login::clientbound::Disconnect {
|
|
||||||
reason: format!(
|
|
||||||
"\"Client Error: Future Version (I'm on {})\"",
|
|
||||||
mc_types::VERSION_NAME,
|
|
||||||
).to_string()
|
|
||||||
}
|
|
||||||
.write(&mut client_conn).await.expect(
|
|
||||||
"Error sending disconnect on: Client on wrong version");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
println!("Connection Closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_play<'a>(
|
|
||||||
mut client_conn: ProtocolConnection<'a>,
|
|
||||||
mut server_conn: ProtocolConnection<'a>,
|
|
||||||
) {
|
|
||||||
let client_conn: &mut ProtocolConnection<'static> =
|
|
||||||
unsafe { mem::transmute(&mut client_conn) };
|
|
||||||
let server_conn: &mut ProtocolConnection<'static> =
|
|
||||||
unsafe { mem::transmute(&mut server_conn) };
|
|
||||||
|
|
||||||
let (mut client_write_conn, mut client_read_conn) =
|
|
||||||
client_conn.split_conn().expect(
|
|
||||||
"Error copying from client to backend");
|
|
||||||
let (mut server_write_conn, mut server_read_conn) =
|
|
||||||
server_conn.split_conn().expect(
|
|
||||||
"Error copying from backend to client");
|
|
||||||
|
|
||||||
// Forward from client to backend
|
|
||||||
let to_backend = tokio::spawn(async move {
|
|
||||||
client_read_conn.forward_play(&mut server_write_conn).await.expect(
|
|
||||||
"Error copying from client to backend");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Forward from backend to client
|
|
||||||
let to_client = tokio::spawn(async move {
|
|
||||||
server_read_conn.forward_play(&mut client_write_conn).await.expect(
|
|
||||||
"Error copying from backend to client");
|
|
||||||
});
|
|
||||||
|
|
||||||
tokio::try_join!(to_backend, to_client).expect(
|
|
||||||
"Error copying between the client and backend");
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Yeahbut May 2024
|
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use std::error::Error;
|
|
||||||
use rsa::RsaPrivateKey;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum OnlineStatus {
|
|
||||||
Online,
|
|
||||||
Offline,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ProxyInfo {
|
|
||||||
pub proxy_addr: String,
|
|
||||||
pub proxy_port: u16,
|
|
||||||
pub online_status: OnlineStatus,
|
|
||||||
pub backend_addr: String,
|
|
||||||
pub backend_port: u16,
|
|
||||||
pub private_key: RsaPrivateKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProxyInfo {
|
|
||||||
pub fn formatted_proxy_address(&self) -> String {
|
|
||||||
format!("{}:{}", self.proxy_addr, self.proxy_port)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn formatted_backend_address(&self) -> String {
|
|
||||||
format!("{}:{}", self.backend_addr, self.backend_port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TcpListenerWrapper {
|
|
||||||
pub listener: TcpListener,
|
|
||||||
pub info: ProxyInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TcpListenerWrapper {
|
|
||||||
pub async fn bind(info: ProxyInfo) -> Result<Self, Box<dyn Error>> {
|
|
||||||
Ok(Self {
|
|
||||||
listener: TcpListener::bind(
|
|
||||||
info.formatted_proxy_address()).await?,
|
|
||||||
info: info,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,17 +4,16 @@ use std::fs;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use purple_cello_mc_protocol::{
|
use purple_cello_mc_protocol::{
|
||||||
mc_types::{self, Result, Packet, ProtocolConnection},
|
mc_types::{self, Result, Packet},
|
||||||
handshake,
|
handshake,
|
||||||
login,
|
login,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::listener;
|
|
||||||
|
|
||||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
||||||
|
|
||||||
struct CachedWhitelist {
|
struct CachedWhitelist {
|
||||||
|
@ -152,49 +151,33 @@ fn check_player_whitelist(player: Player) -> PlayerAllowed {
|
||||||
PlayerAllowed::False("Invalid UUID".to_string())
|
PlayerAllowed::False("Invalid UUID".to_string())
|
||||||
} else if invalid_username {
|
} else if invalid_username {
|
||||||
PlayerAllowed::False(
|
PlayerAllowed::False(
|
||||||
"Invalid Username!\nPlease contact the admins to update your \
|
"Invalid Username, please contact the server admin to update your \
|
||||||
username:\npurplecelloserver@gmail.com".to_string()
|
username: purplecelloserver@gmail.com".to_string()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
PlayerAllowed::False("Not whitelisted on this server.\n\
|
PlayerAllowed::False("Not whitelisted on this server".to_string())
|
||||||
Please direct whitelist requests to the admins:\n\
|
|
||||||
purplecelloserver@gmail.com".to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_player_online(
|
fn check_player(player: Player) -> Result<PlayerAllowed> {
|
||||||
proxy_info: &listener::ProxyInfo,
|
|
||||||
player: Player,
|
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
|
||||||
) -> Result<PlayerAllowed> {
|
|
||||||
let encryption_request = client_conn.create_encryption_request(
|
|
||||||
proxy_info.private_key.clone())?;
|
|
||||||
encryption_request.write(client_conn).await?;
|
|
||||||
let encryption_response =
|
|
||||||
login::serverbound::EncryptionResponse::read(client_conn).await?;
|
|
||||||
client_conn.handle_encryption_response(encryption_response)?;
|
|
||||||
// TODO: Make authentication verification request
|
|
||||||
Ok(check_player_whitelist(player))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_player_offline(player: Player) -> Result<PlayerAllowed> {
|
|
||||||
Ok(check_player_whitelist(player))
|
Ok(check_player_whitelist(player))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn respond_login(
|
pub async fn respond_login(
|
||||||
proxy_info: &listener::ProxyInfo,
|
client_reader: &mut OwnedReadHalf,
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
client_writer: &mut OwnedWriteHalf,
|
||||||
server_conn: &mut ProtocolConnection<'_>,
|
server_reader: &mut OwnedReadHalf,
|
||||||
|
server_writer: &mut OwnedWriteHalf,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let proxy_login = login_to_proxy(proxy_info, client_conn).await?;
|
let proxy_login = login_to_proxy(client_reader).await?;
|
||||||
match proxy_login {
|
match proxy_login {
|
||||||
PlayerAllowed::True(player) => {
|
PlayerAllowed::True(player) => {
|
||||||
println!("Player allowed");
|
println!("Player allowed");
|
||||||
login_to_backend(
|
login_to_backend(
|
||||||
proxy_info,
|
|
||||||
player,
|
player,
|
||||||
client_conn,
|
client_writer,
|
||||||
server_conn,
|
server_reader,
|
||||||
|
server_writer,
|
||||||
).await?;
|
).await?;
|
||||||
return Ok(true)
|
return Ok(true)
|
||||||
},
|
},
|
||||||
|
@ -202,63 +185,57 @@ pub async fn respond_login(
|
||||||
println!("Player blocked: {}", msg);
|
println!("Player blocked: {}", msg);
|
||||||
login::clientbound::Disconnect {
|
login::clientbound::Disconnect {
|
||||||
reason: format!("{{\"text\":\"{}\"}}", msg.to_string())
|
reason: format!("{{\"text\":\"{}\"}}", msg.to_string())
|
||||||
}.write(client_conn).await?;
|
}.write(client_writer).await?;
|
||||||
return Ok(false)
|
return Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn login_to_proxy(
|
async fn login_to_proxy(
|
||||||
proxy_info: &listener::ProxyInfo,
|
client_reader: &mut OwnedReadHalf,
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
|
||||||
) -> Result<PlayerAllowed> {
|
) -> Result<PlayerAllowed> {
|
||||||
println!("Logging into proxy");
|
println!("Logging into proxy");
|
||||||
|
|
||||||
let start_packet =
|
let start_packet =
|
||||||
login::serverbound::LoginStart::read(client_conn).await?;
|
login::serverbound::LoginStart::read(client_reader).await?;
|
||||||
|
|
||||||
let player: Player = Player {
|
let player: Player = Player {
|
||||||
name: start_packet.name,
|
name: start_packet.name,
|
||||||
player_uuid: start_packet.player_uuid,
|
player_uuid: start_packet.player_uuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
match proxy_info.online_status {
|
check_player(player)
|
||||||
listener::OnlineStatus::Online =>
|
|
||||||
check_player_online(proxy_info, player, client_conn).await,
|
|
||||||
listener::OnlineStatus::Offline =>
|
|
||||||
check_player_offline(player),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn login_to_backend(
|
async fn login_to_backend(
|
||||||
proxy_info: &listener::ProxyInfo,
|
|
||||||
player: Player,
|
player: Player,
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
client_writer: &mut OwnedWriteHalf,
|
||||||
server_conn: &mut ProtocolConnection<'_>,
|
server_reader: &mut OwnedReadHalf,
|
||||||
|
server_writer: &mut OwnedWriteHalf,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
println!("Logging into backend");
|
println!("Logging into backend");
|
||||||
handshake::serverbound::Handshake {
|
handshake::serverbound::Handshake {
|
||||||
protocol_version: mc_types::VERSION_PROTOCOL,
|
protocol_version: mc_types::VERSION_PROTOCOL,
|
||||||
server_address: proxy_info.backend_addr.clone(),
|
server_address: "localhost".to_string(),
|
||||||
server_port: proxy_info.backend_port,
|
server_port: 25565,
|
||||||
next_state: 2,
|
next_state: 2,
|
||||||
}.write(server_conn).await?;
|
}.write(server_writer).await?;
|
||||||
|
|
||||||
println!("Login start");
|
println!("Login start");
|
||||||
login::serverbound::LoginStart {
|
login::serverbound::LoginStart {
|
||||||
name: player.name,
|
name: player.name,
|
||||||
player_uuid: player.player_uuid,
|
player_uuid: player.player_uuid,
|
||||||
}.write(server_conn).await?;
|
}.write(server_writer).await?;
|
||||||
|
|
||||||
println!("Finishing backend login");
|
println!("Finishing backend login");
|
||||||
let packet = login::clientbound::LoginSuccess::read(server_conn).await?;
|
let packet = login::clientbound::LoginSuccess::read(server_reader).await?;
|
||||||
|
|
||||||
println!("Finishing proxy login");
|
println!("Finishing proxy login");
|
||||||
login::clientbound::LoginSuccess {
|
login::clientbound::LoginSuccess {
|
||||||
uuid: packet.uuid.clone(),
|
uuid: packet.uuid.clone(),
|
||||||
username: packet.username.clone(),
|
username: packet.username.clone(),
|
||||||
properties: packet.properties.clone(),
|
properties: packet.properties.clone(),
|
||||||
}.write(client_conn).await?;
|
}.write(client_writer).await?;
|
||||||
|
|
||||||
println!("Client logged in");
|
println!("Client logged in");
|
||||||
|
|
||||||
|
|
201
src/main.rs
201
src/main.rs
|
@ -1,57 +1,172 @@
|
||||||
// Yeahbut December 2023
|
// Yeahbut December 2023
|
||||||
|
|
||||||
|
use tokio::net::{TcpListener, TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}};
|
||||||
|
use tokio::io;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use purple_cello_mc_protocol::encrypt;
|
use purple_cello_mc_protocol::{
|
||||||
|
mc_types::{self, Packet},
|
||||||
|
handshake,
|
||||||
|
login,
|
||||||
|
};
|
||||||
|
|
||||||
mod status_handle;
|
mod status_handle;
|
||||||
mod login_handle;
|
mod login_handle;
|
||||||
mod client;
|
|
||||||
mod listener;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let private_key = encrypt::generate_rsa_keys()?;
|
let listener = TcpListener::bind("127.0.0.1:25565").await?;
|
||||||
let offline_info = listener::ProxyInfo{
|
println!("Proxy listening on port 25565...");
|
||||||
proxy_addr: "127.0.0.1".to_string(),
|
|
||||||
proxy_port: 25565,
|
|
||||||
online_status: listener::OnlineStatus::Offline,
|
|
||||||
backend_addr: "127.0.0.1".to_string(),
|
|
||||||
backend_port: 25564,
|
|
||||||
private_key: private_key.clone(),
|
|
||||||
};
|
|
||||||
let online_info = listener::ProxyInfo{
|
|
||||||
proxy_addr: "127.0.0.1".to_string(),
|
|
||||||
proxy_port: 25566,
|
|
||||||
online_status: listener::OnlineStatus::Online,
|
|
||||||
backend_addr: "127.0.0.1".to_string(),
|
|
||||||
backend_port: 25564,
|
|
||||||
private_key: private_key.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let listener_offline: listener::TcpListenerWrapper =
|
while let Ok((client_socket, _)) = listener.accept().await {
|
||||||
listener::TcpListenerWrapper::bind(offline_info).await?;
|
tokio::spawn(handle_client(client_socket));
|
||||||
let listener_online: listener::TcpListenerWrapper =
|
}
|
||||||
listener::TcpListenerWrapper::bind(online_info).await?;
|
|
||||||
|
|
||||||
println!("Proxy listening on port 25565 and 25566...");
|
|
||||||
|
|
||||||
let handle_offline = tokio::spawn(async move{
|
|
||||||
while let Ok((client_socket, _)) = listener_offline
|
|
||||||
.listener.accept().await {
|
|
||||||
tokio::spawn(client::handle_client(
|
|
||||||
client_socket, listener_offline.info.clone()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let handle_online = tokio::spawn(async move{
|
|
||||||
while let Ok((client_socket, _)) = listener_online
|
|
||||||
.listener.accept().await {
|
|
||||||
tokio::spawn(client::handle_client(
|
|
||||||
client_socket, listener_online.info.clone()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tokio::try_join!(handle_offline, handle_online)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_client(client_socket: TcpStream) {
|
||||||
|
println!("Accepting Connection");
|
||||||
|
let backend_addr = "127.0.0.1:25566";
|
||||||
|
|
||||||
|
let (mut client_reader, mut client_writer) = client_socket.into_split();
|
||||||
|
|
||||||
|
// "Failed to connect to the backend server"
|
||||||
|
|
||||||
|
let backend_socket = match TcpStream::connect(backend_addr).await {
|
||||||
|
Ok(backend_socket) => Some(backend_socket.into_split()),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut server_reader, mut server_writer):
|
||||||
|
(Option<OwnedReadHalf>, Option<OwnedWriteHalf>) =
|
||||||
|
match backend_socket {
|
||||||
|
Some(backend_socket) =>
|
||||||
|
(Some(backend_socket.0), Some(backend_socket.1)),
|
||||||
|
None => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer: [u8; 1] = [0; 1];
|
||||||
|
client_reader.peek(&mut buffer)
|
||||||
|
.await.expect("Failed to peek at first byte from stream");
|
||||||
|
let packet_id: u8 = buffer[0];
|
||||||
|
|
||||||
|
if packet_id == 0xFE {
|
||||||
|
status_handle::respond_legacy_status(&mut client_writer)
|
||||||
|
.await.expect("Error handling legacy status request");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
let handshake_packet =
|
||||||
|
handshake::serverbound::Handshake::read(&mut client_reader)
|
||||||
|
.await.expect("Error reading handshake packet");
|
||||||
|
println!("Next state: {}", handshake_packet.next_state);
|
||||||
|
if handshake_packet.next_state == 1 {
|
||||||
|
println!("Receiving Status Request");
|
||||||
|
status_handle::respond_status(
|
||||||
|
&mut client_reader,
|
||||||
|
&mut client_writer,
|
||||||
|
&mut server_reader,
|
||||||
|
&mut server_writer,
|
||||||
|
).await.expect("Error handling status request");
|
||||||
|
return;
|
||||||
|
} else if handshake_packet.next_state == 2 {
|
||||||
|
if handshake_packet.protocol_version == mc_types::VERSION_PROTOCOL {
|
||||||
|
match server_writer {
|
||||||
|
Some(mut server_writer) => {
|
||||||
|
match server_reader {
|
||||||
|
Some(mut server_reader) => {
|
||||||
|
|
||||||
|
if login_handle::respond_login(
|
||||||
|
&mut client_reader,
|
||||||
|
&mut client_writer,
|
||||||
|
&mut server_reader,
|
||||||
|
&mut server_writer,
|
||||||
|
).await.expect(
|
||||||
|
"Error logging into proxy or server"
|
||||||
|
) {
|
||||||
|
handle_play(
|
||||||
|
client_reader,
|
||||||
|
client_writer,
|
||||||
|
server_reader,
|
||||||
|
server_writer,
|
||||||
|
).await;
|
||||||
|
} else {
|
||||||
|
println!("Player blocked from server");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to connect to the backend server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
login::clientbound::Disconnect {
|
||||||
|
reason: "\"Server Error (Server may be starting)\""
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
.write(&mut client_writer)
|
||||||
|
.await
|
||||||
|
.expect("Error sending disconnect on: \
|
||||||
|
Failed to connect to the backend server");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if handshake_packet.protocol_version < mc_types::VERSION_PROTOCOL {
|
||||||
|
println!("Client on outdated version");
|
||||||
|
login::clientbound::Disconnect {
|
||||||
|
reason: format!(
|
||||||
|
"\"Client Error: Outdated Version (I'm on {})\"",
|
||||||
|
mc_types::VERSION_NAME,
|
||||||
|
).to_string()
|
||||||
|
}
|
||||||
|
.write(&mut client_writer).await.expect(
|
||||||
|
"Error sending disconnect on: Client on wrong version");
|
||||||
|
// if handshake_packet.protocol_version > mc_types::VERSION_PROTOCOL
|
||||||
|
} else {
|
||||||
|
println!("Client on future version");
|
||||||
|
login::clientbound::Disconnect {
|
||||||
|
reason: format!(
|
||||||
|
"\"Client Error: Future Version (I'm on {})\"",
|
||||||
|
mc_types::VERSION_NAME,
|
||||||
|
).to_string()
|
||||||
|
}
|
||||||
|
.write(&mut client_writer).await.expect(
|
||||||
|
"Error sending disconnect on: Client on wrong version");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
println!("Connection Closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_play(
|
||||||
|
mut client_reader: OwnedReadHalf,
|
||||||
|
mut client_writer: OwnedWriteHalf,
|
||||||
|
mut server_reader: OwnedReadHalf,
|
||||||
|
mut server_writer: OwnedWriteHalf,
|
||||||
|
) {
|
||||||
|
// Forward from client to backend
|
||||||
|
tokio::spawn(async move {
|
||||||
|
io::copy(
|
||||||
|
&mut client_reader,
|
||||||
|
&mut server_writer,
|
||||||
|
).await.expect(
|
||||||
|
"Error copying from client to backend");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Forward from backend to client
|
||||||
|
tokio::spawn(async move {
|
||||||
|
io::copy(
|
||||||
|
&mut server_reader,
|
||||||
|
&mut client_writer,
|
||||||
|
).await.expect(
|
||||||
|
"Error copying from backend to client");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::io::Read;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
@ -12,13 +13,11 @@ use rand::Rng;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use purple_cello_mc_protocol::{
|
use purple_cello_mc_protocol::{
|
||||||
mc_types::{self, Result, Packet, ProtocolConnection},
|
mc_types::{self, Result, Packet},
|
||||||
handshake,
|
handshake,
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::listener;
|
|
||||||
|
|
||||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
||||||
|
|
||||||
struct CachedMotds {
|
struct CachedMotds {
|
||||||
|
@ -27,10 +26,10 @@ struct CachedMotds {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn online_players(
|
async fn online_players(
|
||||||
proxy_info: listener::ProxyInfo,
|
server_reader: &mut OwnedReadHalf,
|
||||||
server_conn: &mut ProtocolConnection<'_>,
|
server_writer: &mut OwnedWriteHalf,
|
||||||
) -> Result<status::clientbound::StatusPlayers> {
|
) -> Result<status::clientbound::StatusPlayers> {
|
||||||
Ok(get_upstream_status(proxy_info, server_conn).await?.players)
|
Ok(get_upstream_status(server_reader, server_writer).await?.players)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_motds() -> Value {
|
fn load_motds() -> Value {
|
||||||
|
@ -136,28 +135,31 @@ fn favicon() -> Option<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn respond_status(
|
pub async fn respond_status(
|
||||||
proxy_info: listener::ProxyInfo,
|
client_reader: &mut OwnedReadHalf,
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
client_writer: &mut OwnedWriteHalf,
|
||||||
server_conn: &mut Option<ProtocolConnection<'_>>,
|
server_reader: &mut Option<OwnedReadHalf>,
|
||||||
|
server_writer: &mut Option<OwnedWriteHalf>,
|
||||||
)-> Result<()> {
|
)-> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
println!("Status Handling");
|
println!("Status Handling");
|
||||||
let packet =
|
let packet =
|
||||||
status::serverbound::StatusPackets::read(client_conn).await?;
|
status::serverbound::StatusPackets::read(client_reader).await?;
|
||||||
match packet {
|
match packet {
|
||||||
status::serverbound::StatusPackets::Status(_) => {
|
status::serverbound::StatusPackets::Status(_) => {
|
||||||
println!("Handling Status");
|
println!("Handling Status");
|
||||||
let favicon = favicon();
|
let favicon = favicon();
|
||||||
|
|
||||||
let online_players = match server_conn {
|
let online_players = match server_reader {
|
||||||
Some(server_conn) =>
|
Some(server_reader) => match server_writer {
|
||||||
match online_players(
|
Some(server_writer) => match online_players(
|
||||||
proxy_info.clone(),
|
server_reader,
|
||||||
server_conn,
|
server_writer,
|
||||||
).await {
|
).await {
|
||||||
Ok(value) => Some(value),
|
Ok(value) => Some(value),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -203,14 +205,14 @@ pub async fn respond_status(
|
||||||
|
|
||||||
let packet =
|
let packet =
|
||||||
status::clientbound::Status::from_json(status_response)?;
|
status::clientbound::Status::from_json(status_response)?;
|
||||||
packet.write(client_conn).await?;
|
packet.write(client_writer).await?;
|
||||||
},
|
},
|
||||||
status::serverbound::StatusPackets::Ping(packet) => {
|
status::serverbound::StatusPackets::Ping(packet) => {
|
||||||
println!("Handling Ping");
|
println!("Handling Ping");
|
||||||
let new_packet = status::clientbound::Ping{
|
let new_packet = status::clientbound::Ping{
|
||||||
payload: packet.payload,
|
payload: packet.payload,
|
||||||
};
|
};
|
||||||
new_packet.write(client_conn).await?;
|
new_packet.write(client_writer).await?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,27 +221,39 @@ pub async fn respond_status(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_upstream_status(
|
pub async fn get_upstream_status(
|
||||||
proxy_info: listener::ProxyInfo,
|
server_reader: &mut OwnedReadHalf,
|
||||||
server_conn: &mut ProtocolConnection<'_>,
|
server_writer: &mut OwnedWriteHalf,
|
||||||
) -> Result<status::clientbound::StatusResponseData> {
|
) -> Result<status::clientbound::StatusResponseData> {
|
||||||
handshake::serverbound::Handshake{
|
handshake::serverbound::Handshake{
|
||||||
protocol_version: mc_types::VERSION_PROTOCOL,
|
protocol_version: mc_types::VERSION_PROTOCOL,
|
||||||
server_address: proxy_info.backend_addr,
|
server_address: "localhost".to_string(),
|
||||||
server_port: proxy_info.backend_port,
|
server_port: 25565,
|
||||||
next_state: 1,
|
next_state: 1,
|
||||||
}.write(server_conn).await?;
|
}.write(server_writer).await?;
|
||||||
status::serverbound::Status{}.write(server_conn).await?;
|
status::serverbound::Status{}.write(server_writer).await?;
|
||||||
let packet = status::clientbound::Status::read(server_conn).await?;
|
let packet = status::clientbound::Status::read(server_reader).await?;
|
||||||
let status_response = packet.get_json()?;
|
let status_response = packet.get_json()?;
|
||||||
|
|
||||||
|
// mc_types::write_data(server_writer, &mut vec![0]).await?;
|
||||||
|
// let mut data = mc_types::read_data(server_reader).await?;
|
||||||
|
|
||||||
|
// mc_types::get_u8(&mut data);
|
||||||
|
// let json = mc_types::get_string(&mut data)?;
|
||||||
|
// let status_response: status::clientbound::StatusResponseData =
|
||||||
|
// serde_json::from_str(&json)?;
|
||||||
|
|
||||||
|
// let mut out_data: Vec<u8> = vec![1];
|
||||||
|
// out_data.append(&mut mc_types::convert_i64(0));
|
||||||
|
// mc_types::write_packet(server_writer, &mut out_data).await?;
|
||||||
|
|
||||||
Ok(status_response)
|
Ok(status_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn respond_legacy_status(
|
pub async fn respond_legacy_status(
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
client_writer: &mut OwnedWriteHalf,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
println!("Old Style Status");
|
println!("Old Style Status");
|
||||||
client_conn.stream_write.write_u8(0xFF).await?;
|
client_writer.write_u8(0xFF).await?;
|
||||||
|
|
||||||
let s = "§1\0127\0".to_string() +
|
let s = "§1\0127\0".to_string() +
|
||||||
mc_types::VERSION_NAME +
|
mc_types::VERSION_NAME +
|
||||||
|
@ -249,9 +263,9 @@ pub async fn respond_legacy_status(
|
||||||
.flat_map(|c| std::iter::once(c).chain(std::iter::once(0)))
|
.flat_map(|c| std::iter::once(c).chain(std::iter::once(0)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
client_conn.stream_write.write_u16((utf16_vec.len() / 2) as u16).await?;
|
client_writer.write_u16((utf16_vec.len() / 2) as u16).await?;
|
||||||
for utf16_char in utf16_vec {
|
for utf16_char in utf16_vec {
|
||||||
client_conn.stream_write.write_u16(utf16_char).await?;
|
client_writer.write_u16(utf16_char).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue