diff --git a/src/kube_cache.rs b/src/kube_cache.rs index d7e56b0..035667e 100644 --- a/src/kube_cache.rs +++ b/src/kube_cache.rs @@ -11,7 +11,11 @@ use tracing::Instrument; use crate::{ mc_server::{MinecraftAPI, MinecraftServerHandle, ServerDeploymentStatus}, - packets::{clientbound::status::StatusTrait, SendPacket}, + packets::{ + clientbound::status::StatusTrait, + serverbound::handshake::{self}, + SendPacket, + }, OpaqueError, }; @@ -57,15 +61,15 @@ impl KubeCache { self.services.list(&lp).await.unwrap() } - pub async fn query_dep_addr(&self, addr: &str) -> Option { + pub async fn query_dep_addr(&self, addr: &str, port: &str) -> Option { let deploys = self.get_deploys().await; - let result = deploys.iter().find(|x| filter_label_value(x, addr))?; + let result = deploys.iter().find(|x| filter_label_value(x, addr, port))?; Some(result.name()?.to_string()) } - pub async fn query_srv_addr(&self, addr: &str) -> Option { + pub async fn query_srv_addr(&self, addr: &str, port: &str) -> Option { let deploys = self.get_srvs().await; - let result = deploys.iter().find(|x| filter_label_value(x, addr))?; + let result = deploys.iter().find(|x| filter_label_value(x, addr, port))?; Some(result.name()?.to_string()) } @@ -83,9 +87,15 @@ pub struct McApi { } impl MinecraftAPI for McApi { - #[tracing::instrument(name = "MinecraftAPI::query_server", level = "info", skip(self, addr))] - async fn query_server(&self, addr: &str) -> Result { - let dep_name = match self.cache.query_dep_addr(&addr).await { + #[tracing::instrument( + name = "MinecraftAPI::query_server", + level = "info", + skip(self, addr, port) + )] + async fn query_server(&self, addr: &str, port: &str) -> Result { + let addr = sanitize_addr(&addr); + + let dep_name = match self.cache.query_dep_addr(&addr, &port).await { Some(x) => x, None => { return Err(OpaqueError::create(&format!( @@ -93,7 +103,7 @@ impl MinecraftAPI for McApi { ))) } }; - let srv_name = match self.cache.query_srv_addr(&addr).await { + let srv_name = match self.cache.query_srv_addr(&addr, &port).await { Some(x) => x, None => { return Err(OpaqueError::create(&format!( @@ -130,6 +140,7 @@ impl MinecraftAPI for McApi { frequency: Duration, ) -> Result<(), OpaqueError> { let addr = server.get_addr().ok_or("could not get addr of server")?; + let port = server.get_port().ok_or("could not get port of server")?; if self.map.lock().await.get(&addr).is_none() { let span = tracing::span!(parent: None,tracing::Level::INFO, "server_watcher", addr); @@ -137,7 +148,7 @@ impl MinecraftAPI for McApi { async move { tracing::info!("starting watch"); tokio::time::sleep(frequency).await; - let server = self.query_server(&addr).await.unwrap(); + let server = self.query_server(&addr, &port).await.unwrap(); let status_json = match server.query_description().await { Ok(x) => x, Err(e) => { @@ -274,9 +285,7 @@ impl MinecraftServerHandle for Server { } fn get_internal_address(&self) -> Option { - let a = self.srv.clone().spec.unwrap().ports.unwrap(); - let port = a.iter().find(|x| x.name.clone().unwrap() == "mc-router")?; - Some(format!("localhost:{}", port.node_port?)) + Some(format!("localhost:{}", self.get_port()?)) } fn get_addr(&self) -> Option { @@ -324,6 +333,12 @@ impl MinecraftServerHandle for Server { } } } + + fn get_port(&self) -> Option { + let a = self.srv.clone().spec.unwrap().ports.unwrap(); + let port = a.iter().find(|x| x.name.clone().unwrap() == "mc-router")?; + port.node_port.map(|x| x.to_string()) + } } impl Server { @@ -342,11 +357,23 @@ impl Server { } } -fn filter_label_value(dep: &&R, str: &str) -> bool +fn filter_label_value(res: &&R, addr: &str, port: &str) -> bool where R: ResourceExt, { - dep.labels().values().filter(|x| x.as_str() == str).count() > 0 + let mut found_port = false; + res.labels() + .iter() + .filter(|(key, value)| match key.as_str() { + "tami.moe/minecraft" => value.as_str() == addr, + "tami.moe/minecraft-port" => { + found_port = true; + value.as_str() == port + } + _ => false, + }) + .count() + > 0 } impl fmt::Debug for ServerDeploymentStatus { @@ -364,3 +391,34 @@ impl From for OpaqueError { OpaqueError::create(value.to_string().as_str()) } } + +fn terminate_at_null(str: &str) -> &str { + match str.split('\0').next() { + Some(x) => x, + None => str, + } +} + +fn sanitize_addr(addr: &str) -> &str { + // Thanks to a buggy minecraft, when the client sends a join + // from a SRV DNS record, it will not use the address typed + // in the game, but use the address redicted *to* by the + // DNS record as the address for joining, plus a trailing "." + // + // For example: + // server.example.com (_minecraft._tcp.server.example.com) + // (the typed address) I (the DNS SRV record which gets read) + // V + // 5 25565 server.example.com + // I (the response for the DNS SRV query) + // V + // server.example.com. + // (the address used in the protocol) + let addr = addr.trim_end_matches("."); + + // Modded minecraft clients send null terminated strings, + // after which they have extra data. This just removes them + // from the addr lookup + let addr = terminate_at_null(addr); + addr +} diff --git a/src/main.rs b/src/main.rs index 07c02e4..6dcb3d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,7 +109,7 @@ async fn process_connection( Ok(()) } -#[tracing::instrument(level = "info", fields(server_addr = handshake.get_server_address()),skip(client_stream, handshake, api))] +#[tracing::instrument(level = "info", fields(server_addr = handshake.get_server_address(),server_port = handshake.server_port.get_value()),skip(client_stream, handshake, api))] async fn handle_status( client_stream: &mut TcpStream, handshake: &Handshake, @@ -124,13 +124,18 @@ async fn handle_status( }; let server_addr = handshake.get_server_address(); - let server_addr = sanitize_addr(&server_addr); let commit_hash: &'static str = env!("COMMIT_HASH"); let mut status_struct = StatusStructNew::create(); status_struct.version.protocol = handshake.protocol_version.get_int(); let bye_message = format!(" - §dTami§r with §d<3§r §8(rev: {commit_hash})§r"); - let server = match api.query_server(server_addr).await { + let server = match api + .query_server( + &handshake.get_server_address(), + &handshake.server_port.get_value().to_string(), + ) + .await + { Ok(x) => x, Err(e) => { tracing::warn!(err = e.context); @@ -166,8 +171,7 @@ async fn handle_status( ServerDeploymentStatus::Starting | ServerDeploymentStatus::PodOk => { status_struct.players.max = 1; status_struct.players.online = 1; - status_struct.description.text = - format!("§aServer is starting...§r please wait\n{bye_message}"); + status_struct.description.text = format!("\n§2Server is starting...§r{bye_message}"); } ServerDeploymentStatus::Offline => { status_struct.players.max = 1; @@ -181,14 +185,17 @@ async fn handle_status( return mc_server::handle_ping(client_stream).await; } -#[tracing::instrument(level = "info", fields(server_addr = handshake.get_server_address()),skip(client_stream, handshake, api))] +#[tracing::instrument(level = "info", fields(server_addr = handshake.get_server_address(),server_port = handshake.server_port.get_value()),skip(client_stream, handshake, api))] async fn handle_login( client_stream: &mut TcpStream, handshake: &Handshake, api: impl MinecraftAPI, ) -> Result<(), OpaqueError> { let server = api - .query_server(sanitize_addr(&handshake.get_server_address())) + .query_server( + &handshake.get_server_address(), + &handshake.server_port.get_value().to_string(), + ) .await?; let status = server.query_status().await?; @@ -235,34 +242,3 @@ async fn handle_login( } Ok(()) } - -fn terminate_at_null(str: &str) -> &str { - match str.split('\0').next() { - Some(x) => x, - None => str, - } -} - -fn sanitize_addr(addr: &str) -> &str { - // Thanks to a buggy minecraft, when the client sends a join - // from a SRV DNS record, it will not use the address typed - // in the game, but use the address redicted *to* by the - // DNS record as the address for joining, plus a trailing "." - // - // For example: - // server.example.com (_minecraft._tcp.server.example.com) - // (the typed address) I (the DNS SRV record which gets read) - // V - // 5 25565 server.example.com - // I (the response for the DNS SRV query) - // V - // server.example.com. - // (the address used in the protocol) - let addr = addr.trim_end_matches("."); - - // Modded minecraft clients send null terminated strings, - // after which they have extra data. This just removes them - // from the addr lookup - let addr = terminate_at_null(addr); - addr -} diff --git a/src/mc_server.rs b/src/mc_server.rs index e74a044..a1e330a 100644 --- a/src/mc_server.rs +++ b/src/mc_server.rs @@ -1,12 +1,9 @@ -use std::fmt; - use tokio::{io::AsyncWriteExt, net::TcpStream}; use crate::{ - mc_server, packets::{ clientbound::status::{StatusStructNew, StatusTrait}, - serverbound::handshake::Handshake, + serverbound::handshake::{self, Handshake}, Packet, SendPacket, }, OpaqueError, @@ -71,6 +68,7 @@ pub trait MinecraftServerHandle: Clone { async fn query_status(&self) -> Result; fn get_internal_address(&self) -> Option; fn get_addr(&self) -> Option; + fn get_port(&self) -> Option; async fn query_server_connectable(&self) -> Result { let address = self @@ -124,7 +122,7 @@ pub trait MinecraftServerHandle: Clone { } pub trait MinecraftAPI { - async fn query_server(&self, addr: &str) -> Result; + async fn query_server(&self, addr: &str, port: &str) -> Result; async fn start_watch( self, server: impl MinecraftServerHandle,