aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol/src
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-11-21 22:53:38 +0100
committermat <git@matdoes.dev>2025-11-21 22:53:38 +0100
commit6930966aabf9b49fb6a0dc8b61076fa3f1abc298 (patch)
tree52bd3e48c96f0898d0dce494044fbb40915da73f /azalea-protocol/src
parentf464f0152a6450fafc86e1dc993b90e12e395fe7 (diff)
downloadazalea-drasl-6930966aabf9b49fb6a0dc8b61076fa3f1abc298.tar.xz
refactor resolve_address
Diffstat (limited to 'azalea-protocol/src')
-rw-r--r--azalea-protocol/src/lib.rs7
-rw-r--r--azalea-protocol/src/resolve.rs69
-rw-r--r--azalea-protocol/src/resolver.rs74
3 files changed, 75 insertions, 75 deletions
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,
- ))
-}