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