1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#![allow(clippy::module_name_repetitions)]
use alloc::{
borrow::ToOwned,
string::{String, ToString},
vec as a_vec,
};
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
use dns_protocol::{Flags, Question, ResourceRecord};
use embedded_io::{Read, Write};
use psp::sys::in_addr;
use thiserror::Error;
use crate::socket::state::Connected;
use super::{
socket::{udp::UdpSocket, ToSocketAddr},
traits,
};
pub const DNS_PORT: u16 = 53;
lazy_static::lazy_static! {
static ref GOOGLE_DNS_HOST: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), DNS_PORT);
}
/// Create a DNS query for an A record
#[allow(unused)]
#[must_use]
pub fn create_a_type_query(domain: &str) -> Question {
Question::new(domain, dns_protocol::ResourceType::A, 1)
}
/// An error that can occur when using a DNS resolver
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum DnsError {
/// The DNS resolver failed to create
#[error("Failed to create DNS resolver: {}", 0)]
FailedToCreate(String),
/// The hostname could not be resolved
#[error("Hostname resolution failed: {}", 0)]
HostnameResolutionFailed(String),
/// The IP address could not be resolved
#[error("Address resolution failed: {}", 0)]
AddressResolutionFailed(String),
}
/// A DNS resolver
pub struct DnsResolver {
/// The UDP socket that is used to send and receive DNS messages
udp_socket: UdpSocket<Connected>,
/// The DNS server address
dns: SocketAddr,
}
impl DnsResolver {
/// Create a new DNS resolver
///
/// # Parameters
/// - `dns`: The [`SocketAddr`] of the DNS server
///
/// # Errors
/// - [`DnsError::FailedToCreate`]: The DNS resolver failed to create. This may
/// happen if the socket could not be created or bound to the specified address
#[allow(unused)]
pub fn new(dns: SocketAddr) -> Result<Self, DnsError> {
let udp_socket = UdpSocket::new()
.map_err(|_| DnsError::FailedToCreate("Failed to create socket".to_owned()))?;
let udp_socket = udp_socket
.bind(None) // binds to None, otherwise the socket errors for some reason
.map_err(|_| DnsError::FailedToCreate("Failed to bind socket".to_owned()))?;
let udp_socket = udp_socket
.connect(dns)
.map_err(|_| DnsError::FailedToCreate("Failed to connect socket".to_owned()))?;
Ok(DnsResolver { udp_socket, dns })
}
/// Try to create a new DNS resolver with default settings
/// The default settings are to use Google's DNS server at `8.8.8.8:53`
///
/// # Errors
/// - [`DnsError::FailedToCreate`]: The DNS resolver failed to create. This may
/// happen if the socket could not be created or bound to the specified address
pub fn try_default() -> Result<Self, DnsError> {
Self::new(*GOOGLE_DNS_HOST)
}
/// Resolve a hostname to an IP address
///
/// # Parameters
/// - `host`: The hostname to resolve
///
/// # Returns
/// - `Ok(in_addr)`: The IP address of the hostname
/// - `Err(())`: If the hostname could not be resolved
///
/// # Errors
/// - [`DnsError::HostnameResolutionFailed`]: The hostname could not be resolved.
/// This may happen if the connection of the socket fails, or if the DNS server
/// does not answer the query, or any other error occurs
pub fn resolve(&mut self, host: &str) -> Result<in_addr, DnsError> {
// create a new query
let mut questions = [super::dns::create_a_type_query(host)];
let query = dns_protocol::Message::new(
0x42,
Flags::standard_query(),
&mut questions,
&mut [],
&mut [],
&mut [],
);
// create a new buffer with the size of the message
let mut tx_buf = a_vec![0u8; query.space_needed()];
// serialize the message into the buffer
query.write(&mut tx_buf).map_err(|_| {
DnsError::HostnameResolutionFailed("Could not serialize query".to_owned())
})?;
// send the message to the DNS server
let _ = self
.udp_socket
.write(&tx_buf)
.map_err(|e| DnsError::HostnameResolutionFailed(e.to_string()))?;
let mut rx_buf = [0u8; 1024];
// receive the response from the DNS server
let data_len = self
.udp_socket
.read(&mut rx_buf)
.map_err(|e| DnsError::HostnameResolutionFailed(e.to_string()))?;
if data_len == 0 {
return Err(DnsError::HostnameResolutionFailed(
"No data received".to_owned(),
));
}
// parse the response
let mut answers = [ResourceRecord::default(); 16];
let mut authority = [ResourceRecord::default(); 16];
let mut additional = [ResourceRecord::default(); 16];
let message = dns_protocol::Message::read(
&rx_buf[..data_len],
&mut questions,
&mut answers,
&mut authority,
&mut additional,
)
.map_err(|_| DnsError::HostnameResolutionFailed("Could not parse response".to_owned()))?;
if message.answers().is_empty() {
return Err(DnsError::HostnameResolutionFailed(
"No answers received".to_owned(),
));
}
let answer = message.answers()[0];
match answer.data().len() {
4 => {
let mut octets = [0u8; 4];
octets.copy_from_slice(answer.data());
let addr = in_addr(u32::from_be_bytes(octets));
Ok(addr)
}
_ => Err(DnsError::HostnameResolutionFailed(
"Could not parse IP address".to_owned(),
)),
}
}
/// Get the [`SocketAddr`] of the DNS server
#[must_use]
#[inline]
pub fn dns(&self) -> SocketAddr {
self.dns
}
}
impl traits::dns::ResolveHostname for DnsResolver {
type Error = DnsError;
/// Resolve a hostname to an IP address
///
/// # Parameters
/// - `host`: The hostname to resolve
///
/// # Returns
/// - `Ok(SocketAddr)`: The IP address of the hostname
/// - `Err(DnsError)`: If the hostname could not be resolved
///
/// # Errors
/// - [`DnsError::HostnameResolutionFailed`]: The hostname could not be resolved.
/// This may happen if the connection of the socket fails, or if the DNS server
/// does not answer the query, or any other error occurs
fn resolve_hostname(&mut self, hostname: &str) -> Result<SocketAddr, DnsError> {
self.resolve(hostname).map(|addr| addr.to_socket_addr())
}
}
impl traits::dns::ResolveAddr for DnsResolver {
type Error = DnsError;
fn resolve_addr(&mut self, _addr: in_addr) -> Result<String, DnsError> {
todo!("resolve_addr")
}
}
impl traits::dns::DnsResolver for DnsResolver {}