azalea_protocol/
resolver.rs1use std::net::{IpAddr, SocketAddr};
4
5use async_recursion::async_recursion;
6use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider};
7use thiserror::Error;
8
9use crate::ServerAddress;
10
11#[derive(Error, Debug)]
12pub enum ResolverError {
13 #[error("No SRV record found")]
14 NoSrvRecord,
15 #[error("No IP found")]
16 NoIp,
17}
18
19#[must_use]
22#[async_recursion]
23pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> {
24 if let Ok(ip) = address.host.parse::<IpAddr>() {
26 return Ok(SocketAddr::new(ip, address.port));
27 }
28
29 let resolver = TokioResolver::builder(TokioConnectionProvider::default())
33 .unwrap()
34 .build();
35
36 let srv_redirect_result = resolver
38 .srv_lookup(format!("_minecraft._tcp.{}", address.host).as_str())
39 .await;
40
41 if let Ok(redirect_result) = srv_redirect_result {
44 let redirect_srv = redirect_result
45 .iter()
46 .next()
47 .ok_or(ResolverError::NoSrvRecord)?;
48 let redirect_address = ServerAddress {
49 host: redirect_srv.target().to_ascii(),
50 port: redirect_srv.port(),
51 };
52
53 if redirect_address.host == address.host {
54 let lookup_ip_result = resolver.lookup_ip(redirect_address.host).await;
55 let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
56 return Ok(SocketAddr::new(
57 lookup_ip.iter().next().unwrap(),
58 redirect_address.port,
59 ));
60 }
61
62 return resolve_address(&redirect_address).await;
63 }
64
65 let name = Name::from_ascii(&address.host).map_err(|_| ResolverError::NoIp)?;
67 let lookup_ip_result = resolver.lookup_ip(name).await;
68 let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
69
70 Ok(SocketAddr::new(
71 lookup_ip.iter().next().unwrap(),
72 address.port,
73 ))
74}