diff --git a/src/lib.rs b/src/lib.rs index 3f64c99..df152dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,9 +12,5 @@ use diesel::r2d2::{ ConnectionManager }; - -pub mod schema; - - pub static mut DOCKER: LazyLock = LazyLock::new(|| Docker::connect_with_local_defaults().expect("Failed to connect to the docker socket") ); diff --git a/src/main.rs b/src/main.rs index 4914910..94d73dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ /* By: tomoron +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */ -/* Updated: 2026/05/29 21:31:39 by tomoron ### ########.fr */ +/* Updated: 2026/05/31 15:02:01 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -41,10 +41,13 @@ use diesel::{prelude::*, r2d2::{ConnectionManager, Pool}}; use std::env; -//pub mod status; +pub mod schema; +pub mod models; -//use status::ServerStatus; -// +pub mod status; + +use status::ServerStatus; +use crate::models::Servers; pub type DbPool = Pool>; @@ -66,6 +69,8 @@ async fn main() -> io::Result<()> { let mc_listener = TcpListener::bind("0.0.0.0:25565").await?; let rpc_listener = TcpListener::bind("0.0.0.0:8080").await?; +// let servers = schema::servers::table.select(Servers::as_select()).load(conn).unwrap(); + loop { tokio::select! { Ok((socket, _)) = mc_listener.accept() => { diff --git a/src/minecraft/client/buffer.rs b/src/minecraft/client/buffer.rs index 7d17cf9..50d91db 100644 --- a/src/minecraft/client/buffer.rs +++ b/src/minecraft/client/buffer.rs @@ -1,8 +1,6 @@ use crate::minecraft::client::client::Client; use crate::minecraft::varint::varint_read; -use crate::minecraft::handshake::Handshake; -use std::collections::VecDeque; use std::io; @@ -95,28 +93,4 @@ impl Client { Ok(()) } - async fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { - let packet_id = varint_read(&mut packet)?; - - - if self.handshake.is_none() { - if packet_id != 0 { - return Err("packet 0 expected. invalid packet received".to_string()); - } - self.handshake = Some(Handshake::from_packet(&mut packet)?); - - if self.handshake.as_ref().unwrap().intent == 2 { - self.login_intent_handle(&mut packet, packet_id).await?; - } - - return Ok(()); - } - - let intent = self.handshake.as_ref().unwrap().intent; - - if intent == 1 { - self.status_intent_handle(&mut packet, packet_id).await?; - } - Ok(()) - } } diff --git a/src/minecraft/client/client.rs b/src/minecraft/client/client.rs index df57aa8..0df8ac5 100644 --- a/src/minecraft/client/client.rs +++ b/src/minecraft/client/client.rs @@ -6,7 +6,7 @@ /* By: tomoron +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */ -/* Updated: 2026/05/30 00:27:19 by tomoron ### ########.fr */ +/* Updated: 2026/05/31 23:23:05 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,6 +15,8 @@ use crate::minecraft::handshake::Handshake; use crate::minecraft::varint::varint_write; use std::fmt; +use crate::models::Servers; + pub struct Client { pub in_stream: TcpStream, @@ -22,11 +24,22 @@ pub struct Client { pub buffer: Vec, pub out_stream: Option, pub handshake: Option, + + pub closed: bool, + + pub server: Option>, + pub dbPool: DbPool } use crate::DbPool; +use crate::schema; +use crate::models; +use diesel::prelude::*; + +use regex::Regex; + impl Client { pub fn create(stream: TcpStream, pool: DbPool) -> Self { Self { @@ -37,20 +50,40 @@ impl Client { out_stream: None, handshake: None, + closed: false, + + server: None, + dbPool: pool } } - pub async fn send_packet(&self, data: Vec) { - let mut sent_data: Vec = varint_write(data.len() as i32); - - sent_data.extend(data); - let _ = self.in_stream.writable().await; - match self.in_stream.try_write(sent_data.as_slice()) { - Err(e) => { eprintln!("error while sending response {:?}", e); }, - _ => { } + pub async fn get_server(&self) -> Result { + println!("get server name from domain : {}", self.handshake.as_ref().unwrap().server_address); + if let None = self.handshake { + panic!("get server called without a handshake"); } + + + let conn = &mut self.dbPool.get().unwrap(); + + let reg = Regex::new(r"^(?:([a-zA-Z-_]*)\.)?mc\.tmoron\.fr$").unwrap(); + let mut found: Option = None; + if let Some(capture) = reg.captures(&self.handshake.as_ref().unwrap().server_address) { + if let Some(captured_str) = capture.get(1) { + found = Some(captured_str.as_str().to_string()); + } + } + + if let Some(name) = found { + println!("found server name : {}", name); + schema::servers::table.filter(schema::servers::name.eq(name)).select(models::Servers::as_select()).first(conn) + } else { + schema::servers::table.filter(schema::servers::is_default.eq(true)).select(models::Servers::as_select()).first(conn) + + } + } } diff --git a/src/minecraft/client/login.rs b/src/minecraft/client/login.rs index fd86cb2..f941df9 100644 --- a/src/minecraft/client/login.rs +++ b/src/minecraft/client/login.rs @@ -1,26 +1,24 @@ use crate::minecraft::client::client::Client; +use json::object; use crate::minecraft::varint::varint_write; use std::collections::VecDeque; use tokio::net::TcpStream; impl Client { pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque, _packet_id: i32) -> Result<(),String> { - let stream = TcpStream::connect("play.hypixel.net:25565").await.map_err(|_| "failed to connect to remote host".to_string())?; - let mut new_handshake = self.handshake.as_ref().unwrap().clone(); - new_handshake.server_address = "play.hypixel.net".to_string(); - new_handshake.server_port = 25565; + let reason_json = object! { + "type": "text", + "text": "login to start not implemented yet", + "color": "red" + }; + let mut response: Vec = vec![]; + let response_json = json::stringify(reason_json); + let mut response_json_len = varint_write(response_json.len() as i32); - let handshake_packet = new_handshake.to_packet(); - let mut packet = Vec::new(); - packet.extend(varint_write(handshake_packet.len() as i32)); - packet.extend(handshake_packet); - - self.out_stream = Some(stream); - let _ = self.out_stream.as_ref().unwrap().writable().await; - - let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice()); - println!("[{}] sent handshake to server", self); + response.append(&mut response_json_len); + response.append(&mut response_json.as_bytes().into()); + self.send_packet(0x0, response).await; Ok(()) } } diff --git a/src/minecraft/client/mod.rs b/src/minecraft/client/mod.rs index c1a1572..8fe9be8 100644 --- a/src/minecraft/client/mod.rs +++ b/src/minecraft/client/mod.rs @@ -2,3 +2,4 @@ pub mod client; mod buffer; mod status; mod login; +mod packet; diff --git a/src/minecraft/client/packet.rs b/src/minecraft/client/packet.rs new file mode 100644 index 0000000..e8183c9 --- /dev/null +++ b/src/minecraft/client/packet.rs @@ -0,0 +1,80 @@ +use crate::minecraft::client::client::Client; + +use crate::minecraft::varint::{varint_read, varint_write}; +use crate::minecraft::handshake::Handshake; +use crate::models; +use crate::status::ServerStatus; +use std::collections::VecDeque; + +use diesel::result::Error::NotFound; +use tokio::net::TcpStream; + +impl Client { + pub async fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { + let packet_id = varint_read(&mut packet)?; + + + if self.handshake.is_none() { + if packet_id != 0 { + return Err("packet 0 expected. invalid packet received".to_string()); + } + self.handshake = Some(Handshake::from_packet(&mut packet)?); + self.server = Some(self.get_server().await); +// if self.server.as_ref().unwrap().status == ServerStatus::Running { +// return self.proxy_start().await; +// } + + if self.handshake.as_ref().unwrap().intent == 2 { + self.login_intent_handle(&mut packet, packet_id).await?; + } + + return Ok(()); + } + + let intent = self.handshake.as_ref().unwrap().intent; + + if intent == 1 { + self.status_intent_handle(&mut packet, packet_id).await?; + } + Ok(()) + } + + pub async fn send_packet(&self, packet_id: i32, data: Vec) { + let sent_id = varint_write(packet_id); + let mut sent_data: Vec = vec![]; + sent_data.extend(varint_write((data.len() + sent_id.len()) as i32)); + sent_data.extend(sent_id); + + sent_data.extend(data); + let _ = self.in_stream.writable().await; + match self.in_stream.try_write(sent_data.as_slice()) { + Err(e) => { eprintln!("error while sending response {:?}", e); }, + _ => { } + } + } + + + async fn proxy_start(&mut self) -> Result<(),String> { + println!("[{}] starting proxy mode", self); +// let stream = TcpStream::connect(&self.server.as_ref().unwrap().redirect_ip.as_ref().unwrap()).await + let stream = TcpStream::connect("play.hypixel.net").await + .map_err(|_| "failed to connect to remote host".to_string())?; + let mut new_handshake = self.handshake.as_ref().unwrap().clone(); + + new_handshake.server_address = "play.hypixel.net".to_string(); + new_handshake.server_port = 25565; + + let handshake_packet = new_handshake.to_packet(); + let mut packet = Vec::new(); + packet.extend(varint_write(handshake_packet.len() as i32)); + packet.extend(handshake_packet); + + self.out_stream = Some(stream); + + let _ = self.out_stream.as_ref().unwrap().writable().await; + let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice()); + + println!("[{}] sent handshake to server", self); + Ok(()) + } +} diff --git a/src/minecraft/client/status.rs b/src/minecraft/client/status.rs index 3551feb..bb537de 100644 --- a/src/minecraft/client/status.rs +++ b/src/minecraft/client/status.rs @@ -1,5 +1,10 @@ use crate::minecraft::client::client::Client; +use diesel::prelude::*; +use diesel::result::Error::NotFound; +use crate::schema; +use crate::models; + use json::object; use crate::minecraft::varint::varint_write; @@ -16,27 +21,47 @@ impl Client { "max": 420, "online": 69 }, - "description": "§c".to_string() + &self.handshake.as_ref().unwrap().server_address.clone() + "§r:§a" + &self.handshake.as_ref().unwrap().server_port.to_string(), + "description": self.get_description().await, }; + if packet_id == 0 { let mut response : Vec = vec![]; let response_json = json::stringify(status_response); let mut response_json_len = varint_write(response_json.len() as i32); - let mut response_packet_id = varint_write(0); - response.append(&mut response_packet_id); response.append(&mut response_json_len); response.append(&mut response_json.as_bytes().into()); println!("[{}] sent status response to client", self); - self.send_packet(response).await; + self.send_packet(0x0, response).await; } else if packet_id == 1 { - let mut response = varint_write(1); + let mut response = vec![]; response.extend(packet.drain(..)); - self.send_packet(response).await; + self.send_packet(0x1, response).await; println!("[{}] sent ping response to client", self); } Ok(()) } + + + pub async fn get_description(&self) -> String { + if let None = self.server { + "How did you send a status request without a handshake ???".to_string(); + } + + match self.server.as_ref().unwrap() { + Ok(server) => { + let login = match server.last_login { + Some(date) => { format!("{:?}", date) }, + None => { "never".to_string() } + }; + format!("§fServer is {}\nlast login: {}", server.status, login) + }, + Err(NotFound) => { "§cThis server does not exist".to_string() }, + Err(error) => { format!("§cAn error occured while retreiving the information:\n{}", error) } + } + } } + + diff --git a/src/minecraft/socket.rs b/src/minecraft/socket.rs index e0d7ffb..d6debba 100644 --- a/src/minecraft/socket.rs +++ b/src/minecraft/socket.rs @@ -23,6 +23,9 @@ pub async fn process_mc_socket(stream: TcpStream, pool: DbPool) -> Result<(), St } } } + if client.closed { + break; + } } Ok(()) } diff --git a/src/models.rs b/src/models.rs index a66fb5f..329e720 100644 --- a/src/models.rs +++ b/src/models.rs @@ -6,9 +6,10 @@ use serde::Deserialize; use crate::status::ServerStatus; +use crate::schema; #[derive(Queryable, Selectable, Debug)] -#[diesel(table_name = dockermcmgr::schema::servers)] +#[diesel(table_name = schema::servers)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Servers { pub id: i64, @@ -17,17 +18,17 @@ pub struct Servers { pub last_login: Option, pub container_id: Option, pub status: ServerStatus, - pub redirect_ip: Option + pub redirect_ip: Option } #[derive(Deserialize, Insertable)] -#[diesel(table_name = dockermcmgr::schema::servers)] +#[diesel(table_name = schema::servers)] pub struct CreateServer<'a> { pub name: &'a str, pub volume_path: &'a str, pub last_login: Option, pub container_id: Option<&'a str>, pub status: ServerStatus, - pub redirect_ip: Option + pub redirect_ip: Option<&'a str> } diff --git a/src/schema.rs b/src/schema.rs index e24f103..d0c7791 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -11,6 +11,8 @@ diesel::table! { #[max_length = 30] container_id -> Nullable, status -> Int2, - redirect_ip -> Nullable, + is_default -> Bool, + #[max_length = 50] + redirect_ip -> Nullable, } } diff --git a/src/status.rs b/src/status.rs index 642add3..ef64f87 100644 --- a/src/status.rs +++ b/src/status.rs @@ -4,6 +4,7 @@ use diesel::expression::AsExpression; use diesel::pg::Pg; use diesel::serialize::{self, ToSql, Output}; use diesel::sql_types::SmallInt; +use std::fmt::{self, write}; use std::io::Write; #[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow, serde::Serialize, serde::Deserialize)] @@ -14,7 +15,7 @@ pub enum ServerStatus { Stopping = 2, Starting = 3, Running = 4, - Unknown = 5, + Unknown = 5 } impl ToSql for ServerStatus { @@ -42,3 +43,18 @@ impl FromSql for ServerStatus { } } } + +impl fmt::Display for ServerStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str_status = match self { + ServerStatus::Archived => "archieved", + ServerStatus::Stopped => "stopped", + ServerStatus::Stopping => "stopping", + ServerStatus::Starting => "starting", + ServerStatus::Running => "running", + ServerStatus::Unknown => "(error : unknown status)" + + }; + write!(f, "{}", str_status) + } +}