1#![allow(clippy::module_name_repetitions)]
2
3use alloc::{
4 borrow::ToOwned,
5 string::{String, ToString},
6 vec as a_vec,
7};
8use core::net::{IpAddr, Ipv4Addr, SocketAddr};
9use dns_protocol::{Flags, Question, ResourceRecord};
10use embedded_io::{Read, Write};
11use psp::sys::in_addr;
12use thiserror::Error;
13
14use crate::socket::state::Connected;
15
16use super::{
17 socket::{udp::UdpSocket, ToSocketAddr},
18 traits,
19};
20
21pub const DNS_PORT: u16 = 53;
22lazy_static::lazy_static! {
23 static ref GOOGLE_DNS_HOST: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), DNS_PORT);
24}
25
26#[allow(unused)]
28#[must_use]
29pub fn create_a_type_query(domain: &str) -> Question {
30 Question::new(domain, dns_protocol::ResourceType::A, 1)
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Error)]
35pub enum DnsError {
36 #[error("Failed to create DNS resolver: {}", 0)]
38 FailedToCreate(String),
39 #[error("Hostname resolution failed: {}", 0)]
41 HostnameResolutionFailed(String),
42 #[error("Address resolution failed: {}", 0)]
44 AddressResolutionFailed(String),
45}
46
47pub struct DnsResolver {
49 udp_socket: UdpSocket<Connected>,
51 dns: SocketAddr,
53}
54
55impl DnsResolver {
56 #[allow(unused)]
65 pub fn new(dns: SocketAddr) -> Result<Self, DnsError> {
66 let udp_socket = UdpSocket::new()
67 .map_err(|_| DnsError::FailedToCreate("Failed to create socket".to_owned()))?;
68 let udp_socket = udp_socket
69 .bind(None) .map_err(|_| DnsError::FailedToCreate("Failed to bind socket".to_owned()))?;
71 let udp_socket = udp_socket
72 .connect(dns)
73 .map_err(|_| DnsError::FailedToCreate("Failed to connect socket".to_owned()))?;
74
75 Ok(DnsResolver { udp_socket, dns })
76 }
77
78 pub fn try_default() -> Result<Self, DnsError> {
85 Self::new(*GOOGLE_DNS_HOST)
86 }
87
88 pub fn resolve(&mut self, host: &str) -> Result<in_addr, DnsError> {
102 let mut questions = [super::dns::create_a_type_query(host)];
104 let query = dns_protocol::Message::new(
105 0x42,
106 Flags::standard_query(),
107 &mut questions,
108 &mut [],
109 &mut [],
110 &mut [],
111 );
112
113 let mut tx_buf = a_vec![0u8; query.space_needed()];
115 query.write(&mut tx_buf).map_err(|_| {
117 DnsError::HostnameResolutionFailed("Could not serialize query".to_owned())
118 })?;
119
120 let _ = self
122 .udp_socket
123 .write(&tx_buf)
124 .map_err(|e| DnsError::HostnameResolutionFailed(e.to_string()))?;
125
126 let mut rx_buf = [0u8; 1024];
127
128 let data_len = self
130 .udp_socket
131 .read(&mut rx_buf)
132 .map_err(|e| DnsError::HostnameResolutionFailed(e.to_string()))?;
133
134 if data_len == 0 {
135 return Err(DnsError::HostnameResolutionFailed(
136 "No data received".to_owned(),
137 ));
138 }
139
140 let mut answers = [ResourceRecord::default(); 16];
142 let mut authority = [ResourceRecord::default(); 16];
143 let mut additional = [ResourceRecord::default(); 16];
144 let message = dns_protocol::Message::read(
145 &rx_buf[..data_len],
146 &mut questions,
147 &mut answers,
148 &mut authority,
149 &mut additional,
150 )
151 .map_err(|_| DnsError::HostnameResolutionFailed("Could not parse response".to_owned()))?;
152
153 if message.answers().is_empty() {
154 return Err(DnsError::HostnameResolutionFailed(
155 "No answers received".to_owned(),
156 ));
157 }
158 let answer = message.answers()[0];
159
160 match answer.data().len() {
161 4 => {
162 let mut octets = [0u8; 4];
163 octets.copy_from_slice(answer.data());
164 let addr = in_addr(u32::from_be_bytes(octets));
165 Ok(addr)
166 }
167 _ => Err(DnsError::HostnameResolutionFailed(
168 "Could not parse IP address".to_owned(),
169 )),
170 }
171 }
172
173 #[must_use]
175 #[inline]
176 pub fn dns(&self) -> SocketAddr {
177 self.dns
178 }
179}
180
181impl traits::dns::ResolveHostname for DnsResolver {
182 type Error = DnsError;
183
184 fn resolve_hostname(&mut self, hostname: &str) -> Result<SocketAddr, DnsError> {
198 self.resolve(hostname).map(|addr| addr.to_socket_addr())
199 }
200}
201
202impl traits::dns::ResolveAddr for DnsResolver {
203 type Error = DnsError;
204
205 fn resolve_addr(&mut self, _addr: in_addr) -> Result<String, DnsError> {
206 todo!("resolve_addr")
207 }
208}
209
210impl traits::dns::DnsResolver for DnsResolver {}