psp_net/
dns.rs

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/// Create a DNS query for an A record
27#[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/// An error that can occur when using a DNS resolver
34#[derive(Debug, Clone, PartialEq, Eq, Error)]
35pub enum DnsError {
36    /// The DNS resolver failed to create
37    #[error("Failed to create DNS resolver: {}", 0)]
38    FailedToCreate(String),
39    /// The hostname could not be resolved
40    #[error("Hostname resolution failed: {}", 0)]
41    HostnameResolutionFailed(String),
42    /// The IP address could not be resolved
43    #[error("Address resolution failed: {}", 0)]
44    AddressResolutionFailed(String),
45}
46
47/// A DNS resolver
48pub struct DnsResolver {
49    /// The UDP socket that is used to send and receive DNS messages
50    udp_socket: UdpSocket<Connected>,
51    /// The DNS server address
52    dns: SocketAddr,
53}
54
55impl DnsResolver {
56    /// Create a new DNS resolver
57    ///
58    /// # Parameters
59    /// - `dns`: The [`SocketAddr`] of the DNS server
60    ///
61    /// # Errors
62    /// - [`DnsError::FailedToCreate`]: The DNS resolver failed to create. This may
63    ///   happen if the socket could not be created or bound to the specified address
64    #[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) // binds to None, otherwise the socket errors for some reason
70            .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    /// Try to create a new DNS resolver with default settings
79    /// The default settings are to use Google's DNS server at `8.8.8.8:53`
80    ///
81    /// # Errors
82    /// - [`DnsError::FailedToCreate`]: The DNS resolver failed to create. This may
83    ///   happen if the socket could not be created or bound to the specified address
84    pub fn try_default() -> Result<Self, DnsError> {
85        Self::new(*GOOGLE_DNS_HOST)
86    }
87
88    /// Resolve a hostname to an IP address
89    ///
90    /// # Parameters
91    /// - `host`: The hostname to resolve
92    ///
93    /// # Returns
94    /// - `Ok(in_addr)`: The IP address of the hostname
95    /// - `Err(())`: If the hostname could not be resolved
96    ///
97    /// # Errors
98    /// - [`DnsError::HostnameResolutionFailed`]: The hostname could not be resolved.
99    ///   This may happen if the connection of the socket fails, or if the DNS server
100    ///   does not answer the query, or any other error occurs
101    pub fn resolve(&mut self, host: &str) -> Result<in_addr, DnsError> {
102        // create a new query
103        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        // create a new buffer with the size of the message
114        let mut tx_buf = a_vec![0u8; query.space_needed()];
115        // serialize the message into the buffer
116        query.write(&mut tx_buf).map_err(|_| {
117            DnsError::HostnameResolutionFailed("Could not serialize query".to_owned())
118        })?;
119
120        // send the message to the DNS server
121        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        // receive the response from the DNS server
129        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        // parse the response
141        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    /// Get the [`SocketAddr`] of the DNS server
174    #[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    /// Resolve a hostname to an IP address
185    ///
186    /// # Parameters
187    /// - `host`: The hostname to resolve
188    ///
189    /// # Returns
190    /// - `Ok(SocketAddr)`: The IP address of the hostname
191    /// - `Err(DnsError)`: If the hostname could not be resolved
192    ///
193    /// # Errors
194    /// - [`DnsError::HostnameResolutionFailed`]: The hostname could not be resolved.
195    ///   This may happen if the connection of the socket fails, or if the DNS server
196    ///   does not answer the query, or any other error occurs
197    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 {}