azalea_protocol/
resolve.rs

1//! Resolve a Minecraft server address into an IP address and port.
2
3use std::{
4    net::{IpAddr, SocketAddr},
5    sync::LazyLock,
6};
7
8pub use hickory_resolver::ResolveError;
9use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider};
10
11use crate::address::ServerAddr;
12
13#[doc(hidden)]
14#[deprecated(note = "Renamed to ResolveError")]
15pub type ResolverError = ResolveError;
16
17static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
18    TokioResolver::builder(TokioConnectionProvider::default())
19        .unwrap()
20        .build()
21});
22
23/// Resolve a Minecraft server address into an IP address and port.
24///
25/// If it's already an IP address, it's returned as-is.
26pub async fn resolve_address(mut address: &ServerAddr) -> Result<SocketAddr, ResolveError> {
27    let redirect = resolve_srv_redirect(address).await;
28    if let Ok(redirect_target) = &redirect {
29        address = redirect_target;
30    }
31
32    resolve_ip_without_redirects(address).await
33}
34
35async fn resolve_ip_without_redirects(address: &ServerAddr) -> Result<SocketAddr, ResolveError> {
36    if let Ok(ip) = address.host.parse::<IpAddr>() {
37        // no need to do a lookup
38        return Ok(SocketAddr::new(ip, address.port));
39    }
40
41    let name = Name::from_ascii(&address.host)?;
42    let lookup_ip = RESOLVER.lookup_ip(name).await?;
43
44    let ip = lookup_ip
45        .iter()
46        .next()
47        .ok_or(hickory_resolver::ResolveError::from(
48            "No A/AAAA record found",
49        ))?;
50
51    Ok(SocketAddr::new(ip, address.port))
52}
53
54async fn resolve_srv_redirect(address: &ServerAddr) -> Result<ServerAddr, ResolveError> {
55    if address.port != 25565 {
56        return Err(ResolveError::from("Port must be 25565 to do a SRV lookup"));
57    }
58
59    let query = format!("_minecraft._tcp.{}", address.host);
60    let res = RESOLVER.srv_lookup(query).await?;
61
62    let srv = res
63        .iter()
64        .next()
65        .ok_or(ResolveError::from("No SRV record found"))?;
66    Ok(ServerAddr {
67        host: srv.target().to_ascii(),
68        port: srv.port(),
69    })
70}