diff options
| author | mat <git@matdoes.dev> | 2025-11-21 22:53:38 +0100 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2025-11-21 22:53:38 +0100 |
| commit | 6930966aabf9b49fb6a0dc8b61076fa3f1abc298 (patch) | |
| tree | 52bd3e48c96f0898d0dce494044fbb40915da73f | |
| parent | f464f0152a6450fafc86e1dc993b90e12e395fe7 (diff) | |
| download | azalea-drasl-6930966aabf9b49fb6a0dc8b61076fa3f1abc298.tar.xz | |
refactor resolve_address
| -rw-r--r-- | CHANGELOG.md | 5 | ||||
| -rw-r--r-- | Cargo.lock | 12 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | azalea-client/src/client.rs | 10 | ||||
| -rw-r--r-- | azalea-client/src/ping.rs | 8 | ||||
| -rw-r--r-- | azalea-protocol/Cargo.toml | 1 | ||||
| -rw-r--r-- | azalea-protocol/src/lib.rs | 7 | ||||
| -rw-r--r-- | azalea-protocol/src/resolve.rs | 69 | ||||
| -rw-r--r-- | azalea-protocol/src/resolver.rs | 74 | ||||
| -rw-r--r-- | azalea/src/lib.rs | 4 | ||||
| -rw-r--r-- | azalea/src/swarm/mod.rs | 4 |
11 files changed, 92 insertions, 103 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 03459fc9..ad88fbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,20 +23,23 @@ is breaking anyways, semantic versioning is not followed. - Swap the order of the type parameters in entity filtering functions so query is first, then filter. - Add optional `timeout_ticks` field to `Client::open_container_at`. - Rename `ResourceLocation` to `Identifier` to match Minecraft's new internal naming. +- Rename `azalea_protocol::resolver` to `resolve` and `ResolverError` to `ResolveError`. ### Fixed - The wrong path was temporarily executed if we received a `GotoEvent` while the path that's being executed was more than 50 nodes long. - The pathfinder can now jump from dirt path and farmland blocks correctly. - Don't panic when receiving an unexpected `PathFoundEvent`. (@Hiradpi) -- Don't panic when the `LocalPlayerEvents` component is missing. (@suprohub) - The pathfinder sometimes got stuck when going up stairs that are facing the wrong direction. - ReachBlockPosGoal had the wrong cost when the destination is surrounded in blocks. - Some parkour movements had the wrong costs. - The pathfinder no longer spins when descending more than one block. - The pathfinder now avoids slipping off when the last block of the path is on ice. +- Don't panic when the `LocalPlayerEvents` component is missing. (@suprohub) - The 'with' field in formatted text didn't correctly support mixed types. (@Tert0) - The WritableBookContent and ResolvableProfile data components had the wrong protocol implementations. +- Resolving server addresses shouldn't be recursive. + ## [0.14.0+mc1.21.8] - 2025-09-28 @@ -162,17 +162,6 @@ dependencies = [ ] [[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "async-task" version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -490,7 +479,6 @@ name = "azalea-protocol" version = "0.14.0+mc1.21.10" dependencies = [ "anyhow", - "async-recursion", "azalea-auth", "azalea-block", "azalea-brigadier", @@ -32,7 +32,6 @@ repository = "https://github.com/azalea-rs/azalea" aes = "0.8.4" anyhow = "1.0.100" async-compat = "0.2.5" -async-recursion = "1.1.1" base64 = "0.22.1" bevy_app = "0.17.2" bevy_ecs = { version = "0.17.2", default-features = false } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index e0902779..20e1cb3b 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -23,7 +23,7 @@ use azalea_protocol::{ ServerAddress, connect::Proxy, packets::{Packet, game::ServerboundGamePacket}, - resolver, + resolve, }; use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance}; use bevy_app::{App, AppExit, Plugin, PluginsState, SubApp, Update}; @@ -88,8 +88,8 @@ pub struct Client { /// An error that happened while joining the server. #[derive(Error, Debug)] pub enum JoinError { - #[error("{0}")] - Resolver(#[from] resolver::ResolverError), + #[error(transparent)] + Resolver(#[from] resolve::ResolveError), #[error("The given address could not be parsed into a ServerAddress")] InvalidAddress, } @@ -173,7 +173,7 @@ impl Client { address: impl TryInto<ServerAddress>, ) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> { let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?; - let resolved_address = resolver::resolve_address(&address).await?; + let resolved_address = resolve::resolve_address(&address).await?; let (tx, rx) = mpsc::unbounded_channel(); let client = Self::start_client(StartClientOpts::new( @@ -192,7 +192,7 @@ impl Client { proxy: Proxy, ) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> { let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?; - let resolved_address = resolver::resolve_address(&address).await?; + let resolved_address = resolve::resolve_address(&address).await?; let (tx, rx) = mpsc::unbounded_channel(); let client = Self::start_client( diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs index 8c4e2240..93018a82 100644 --- a/azalea-client/src/ping.rs +++ b/azalea-client/src/ping.rs @@ -16,14 +16,14 @@ use azalea_protocol::{ s_status_request::ServerboundStatusRequest, }, }, - resolver, + resolve, }; use thiserror::Error; #[derive(Error, Debug)] pub enum PingError { #[error("{0}")] - Resolver(#[from] resolver::ResolverError), + Resolver(#[from] resolve::ResolveError), #[error("{0}")] Connection(#[from] ConnectionError), #[error("{0}")] @@ -51,7 +51,7 @@ pub async fn ping_server( address: impl TryInto<ServerAddress>, ) -> Result<ClientboundStatusResponse, PingError> { let address: ServerAddress = address.try_into().map_err(|_| PingError::InvalidAddress)?; - let resolved_address = resolver::resolve_address(&address).await?; + let resolved_address = resolve::resolve_address(&address).await?; let conn = Connection::new(&resolved_address).await?; ping_server_with_connection(address, conn).await } @@ -62,7 +62,7 @@ pub async fn ping_server_with_proxy( proxy: Proxy, ) -> Result<ClientboundStatusResponse, PingError> { let address: ServerAddress = address.try_into().map_err(|_| PingError::InvalidAddress)?; - let resolved_address = resolver::resolve_address(&address).await?; + let resolved_address = resolve::resolve_address(&address).await?; let conn = Connection::new_with_proxy(&resolved_address, proxy).await?; ping_server_with_connection(address, conn).await } diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index df3aa615..1ab25a12 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -12,7 +12,6 @@ tracing.workspace = true tracing-subscriber.workspace = true [dependencies] -async-recursion.workspace = true azalea-auth.workspace = true azalea-block.workspace = true azalea-brigadier = { workspace = true, features = ["azalea-buf"] } diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index fdba34bc..45abf240 100644 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -24,9 +24,14 @@ pub mod connect; #[cfg(feature = "packets")] pub mod packets; pub mod read; -pub mod resolver; +pub mod resolve; pub mod write; +#[deprecated(note = "Renamed to resolve")] +pub mod resolver { + pub use super::resolve::*; +} + /// A host and port. It's possible that the port doesn't resolve to anything. /// /// # Examples diff --git a/azalea-protocol/src/resolve.rs b/azalea-protocol/src/resolve.rs new file mode 100644 index 00000000..f18a04a8 --- /dev/null +++ b/azalea-protocol/src/resolve.rs @@ -0,0 +1,69 @@ +//! Resolve a Minecraft server address into an IP address and port. + +use std::{ + net::{IpAddr, SocketAddr}, + sync::LazyLock, +}; + +pub use hickory_resolver::ResolveError; +use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider}; + +use crate::ServerAddress; + +#[deprecated(note = "Renamed to ResolveError")] +pub type ResolverError = ResolveError; + +static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| { + TokioResolver::builder(TokioConnectionProvider::default()) + .unwrap() + .build() +}); + +/// Resolve a Minecraft server address into an IP address and port. +/// +/// If it's already an IP address, it's returned as-is. +pub async fn resolve_address(mut address: &ServerAddress) -> Result<SocketAddr, ResolveError> { + let redirect = resolve_srv_redirect(address).await; + if let Ok(redirect_target) = &redirect { + address = redirect_target; + } + + resolve_ip_without_redirects(address).await +} + +async fn resolve_ip_without_redirects(address: &ServerAddress) -> Result<SocketAddr, ResolveError> { + if let Ok(ip) = address.host.parse::<IpAddr>() { + // no need to do a lookup + return Ok(SocketAddr::new(ip, address.port)); + } + + let name = Name::from_ascii(&address.host)?; + let lookup_ip = RESOLVER.lookup_ip(name).await?; + + let ip = lookup_ip + .iter() + .next() + .ok_or(hickory_resolver::ResolveError::from( + "No A/AAAA record found", + ))?; + + Ok(SocketAddr::new(ip, address.port)) +} + +async fn resolve_srv_redirect(address: &ServerAddress) -> Result<ServerAddress, ResolveError> { + if address.port != 25565 { + return Err(ResolveError::from("Port must be 25565 to do a SRV lookup")); + } + + let query = format!("_minecraft._tcp.{}", address.host); + let res = RESOLVER.srv_lookup(query).await?; + + let srv = res + .iter() + .next() + .ok_or(ResolveError::from("No SRV record found"))?; + Ok(ServerAddress { + host: srv.target().to_ascii(), + port: srv.port(), + }) +} diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs deleted file mode 100644 index 4cd2faad..00000000 --- a/azalea-protocol/src/resolver.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! Resolve IPs from hostnames. - -use std::net::{IpAddr, SocketAddr}; - -use async_recursion::async_recursion; -use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider}; -use thiserror::Error; - -use crate::ServerAddress; - -#[derive(Error, Debug)] -pub enum ResolverError { - #[error("No SRV record found")] - NoSrvRecord, - #[error("No IP found")] - NoIp, -} - -/// Resolve a Minecraft server address into an IP address and port. -/// If it's already an IP address, it's returned as-is. -#[must_use] -#[async_recursion] -pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> { - // If the address.host is already in the format of an ip address, return it. - if let Ok(ip) = address.host.parse::<IpAddr>() { - return Ok(SocketAddr::new(ip, address.port)); - } - - // we specify Cloudflare instead of the default resolver because - // hickory_resolver has an issue on Windows where it's really slow using the - // default resolver - let resolver = TokioResolver::builder(TokioConnectionProvider::default()) - .unwrap() - .build(); - - // first, we do a srv lookup for _minecraft._tcp.<host> - let srv_redirect_result = resolver - .srv_lookup(format!("_minecraft._tcp.{}", address.host).as_str()) - .await; - - // if it resolves that means it's a redirect so we call resolve_address again - // with the new host - if let Ok(redirect_result) = srv_redirect_result { - let redirect_srv = redirect_result - .iter() - .next() - .ok_or(ResolverError::NoSrvRecord)?; - let redirect_address = ServerAddress { - host: redirect_srv.target().to_ascii(), - port: redirect_srv.port(), - }; - - if redirect_address.host == address.host { - let lookup_ip_result = resolver.lookup_ip(redirect_address.host).await; - let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?; - return Ok(SocketAddr::new( - lookup_ip.iter().next().unwrap(), - redirect_address.port, - )); - } - - return resolve_address(&redirect_address).await; - } - - // there's no redirect, try to resolve this as an ip address - let name = Name::from_ascii(&address.host).map_err(|_| ResolverError::NoIp)?; - let lookup_ip_result = resolver.lookup_ip(name).await; - let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?; - - Ok(SocketAddr::new( - lookup_ip.iter().next().unwrap(), - address.port, - )) -} diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index f4f0844d..0fec9fc0 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -39,7 +39,7 @@ use bevy_app::AppExit; pub use bevy_ecs as ecs; use ecs::component::Component; use futures::{Future, future::BoxFuture}; -use protocol::{ServerAddress, connect::Proxy, resolver::ResolverError}; +use protocol::{ServerAddress, connect::Proxy, resolve::ResolveError}; use swarm::SwarmBuilder; use thiserror::Error; @@ -55,7 +55,7 @@ pub enum StartError { #[error("Invalid address")] InvalidAddress, #[error(transparent)] - ResolveAddress(#[from] ResolverError), + ResolveAddress(#[from] ResolveError), } /// A builder for creating new [`Client`]s. This is the recommended way of diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 34247453..d49a5190 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -26,7 +26,7 @@ use azalea_client::{ start_ecs_runner, }; use azalea_entity::LocalEntity; -use azalea_protocol::{ServerAddress, resolver}; +use azalea_protocol::{ServerAddress, resolve}; use azalea_world::InstanceContainer; use bevy_app::{App, AppExit, PluginGroup, PluginGroupBuilder, Plugins, SubApp}; use bevy_ecs::prelude::*; @@ -445,7 +445,7 @@ where let resolved_address = if let Some(a) = default_join_opts.custom_resolved_address { a } else { - resolver::resolve_address(&address).await? + resolve::resolve_address(&address).await? }; let instance_container = Arc::new(RwLock::new(InstanceContainer::default())); |
