1use futures::lock::Mutex;
8use futures::stream::StreamExt;
9use hickory_proto::op::{Message, OpCode, Query, ResponseCode};
10use hickory_proto::rr::{DNSClass, Name, RData, Record, RecordType, rdata};
11use hickory_proto::serialize::binary::{BinDecodable, BinEncodable};
12use std::collections::HashMap;
13use std::net::{IpAddr, SocketAddr};
14use std::sync::Arc;
15use tor_rtcompat::{SpawnExt, UdpProvider};
16use tracing::{debug, error, info, warn};
17
18use arti_client::{Error, HasKind, StreamPrefs, TorClient};
19use safelog::sensitive as sv;
20use tor_config::Listen;
21use tor_error::{error_report, warn_report};
22use tor_rtcompat::{Runtime, UdpSocket};
23
24use anyhow::{Result, anyhow};
25
26use crate::proxy::port_info;
27
28const MAX_DATAGRAM_SIZE: usize = 1536;
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36struct DnsIsolationKey(usize, IpAddr);
37
38impl arti_client::isolation::IsolationHelper for DnsIsolationKey {
39 fn compatible_same_type(&self, other: &Self) -> bool {
40 self == other
41 }
42
43 fn join_same_type(&self, other: &Self) -> Option<Self> {
44 if self == other {
45 Some(self.clone())
46 } else {
47 None
48 }
49 }
50
51 fn enables_long_lived_circuits(&self) -> bool {
52 false
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58struct DnsCacheKey(DnsIsolationKey, Vec<Query>);
59
60#[derive(Debug, Clone)]
62struct DnsResponseTarget<U> {
63 id: u16,
65 addr: SocketAddr,
67 socket: Arc<U>,
69}
70
71async fn do_query<R>(
73 tor_client: &TorClient<R>,
74 query: &Query,
75 prefs: &StreamPrefs,
76) -> Result<Vec<Record>, ResponseCode>
77where
78 R: Runtime,
79{
80 let mut answers = Vec::new();
81
82 let err_conv = |error: Error| {
83 if tor_error::ErrorKind::RemoteHostNotFound == error.kind() {
84 ResponseCode::NoError
86 } else {
87 ResponseCode::ServFail
88 }
89 };
90
91 let mut a = Vec::new();
92 let mut ptr = Vec::new();
93
94 match query.query_class() {
95 DNSClass::IN => {
96 match query.query_type() {
97 typ @ RecordType::A | typ @ RecordType::AAAA => {
98 let mut name = query.name().clone();
99 name.set_fqdn(false);
101 let res = tor_client
102 .resolve_with_prefs(&name.to_utf8(), prefs)
103 .await
104 .map_err(err_conv)?;
105 for ip in res {
106 a.push((query.name().clone(), ip, typ));
107 }
108 }
109 RecordType::PTR => {
110 let addr = query
111 .name()
112 .parse_arpa_name()
113 .map_err(|_| ResponseCode::FormErr)?
114 .addr();
115 let res = tor_client
116 .resolve_ptr_with_prefs(addr, prefs)
117 .await
118 .map_err(err_conv)?;
119 for domain in res {
120 let domain = Name::from_utf8(domain).map_err(|_| ResponseCode::ServFail)?;
121 ptr.push((query.name().clone(), domain));
122 }
123 }
124 _ => {
125 return Err(ResponseCode::NotImp);
126 }
127 }
128 }
129 _ => {
130 return Err(ResponseCode::NotImp);
131 }
132 }
133 for (name, ip, typ) in a {
134 match (ip, typ) {
135 (IpAddr::V4(v4), RecordType::A) => {
136 answers.push(Record::from_rdata(name, 3600, RData::A(rdata::A(v4))));
137 }
138 (IpAddr::V6(v6), RecordType::AAAA) => {
139 answers.push(Record::from_rdata(name, 3600, RData::AAAA(rdata::AAAA(v6))));
140 }
141 _ => (),
142 }
143 }
144 for (ptr, name) in ptr {
145 answers.push(Record::from_rdata(ptr, 3600, RData::PTR(rdata::PTR(name))));
146 }
147
148 Ok(answers)
149}
150
151async fn handle_dns_req<R, U>(
154 tor_client: &TorClient<R>,
155 socket_id: usize,
156 packet: &[u8],
157 addr: SocketAddr,
158 socket: Arc<U>,
159 current_requests: &Mutex<HashMap<DnsCacheKey, Vec<DnsResponseTarget<U>>>>,
160) -> Result<()>
161where
162 R: Runtime,
163 U: UdpSocket,
164{
165 let query = Message::from_bytes(packet)?;
167 let id = query.metadata.id;
168 let queries = query.queries;
169 let isolation = DnsIsolationKey(socket_id, addr.ip());
170
171 let request_id = {
172 let request_id = DnsCacheKey(isolation.clone(), queries.clone());
173
174 let response_target = DnsResponseTarget { id, addr, socket };
175
176 let mut current_requests = current_requests.lock().await;
177
178 let req = current_requests.entry(request_id.clone()).or_default();
179 req.push(response_target);
180
181 if req.len() > 1 {
182 debug!("Received a query already being served");
183 return Ok(());
184 }
185 debug!("Received a new query");
186
187 request_id
188 };
189
190 let mut response: Message;
191 if queries.len() != 1 {
194 response = Message::error_msg(id, OpCode::Query, ResponseCode::FormErr);
195 } else {
196 let mut prefs = StreamPrefs::new();
197 prefs.set_isolation(isolation);
198
199 response = match do_query(tor_client, &queries[0], &prefs).await {
200 Ok(answers) => {
201 let mut response = Message::response(id, OpCode::Query);
202 response.metadata.recursion_desired = query.metadata.recursion_desired;
203 response.metadata.recursion_available = true;
204 response.add_queries(queries).add_answers(answers);
205 response
207 }
208 Err(error_type) => Message::error_msg(id, OpCode::Query, error_type),
209 };
210 }
211
212 let targets = current_requests
214 .lock()
215 .await
216 .remove(&request_id)
217 .unwrap_or_default();
218
219 for target in targets {
220 response.metadata.id = target.id;
221 let response = match response.to_bytes() {
223 Ok(r) => r,
224 Err(e) => {
225 error_report!(e, "Failed to serialize DNS packet: {:?}", sv(&response));
230 continue;
231 }
232 };
233 let _ = target.socket.send(&response, &target.addr).await;
234 }
235 Ok(())
236}
237
238#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
240#[must_use]
241pub(crate) struct DnsProxy<R: Runtime> {
242 udp_sockets: Vec<<R as UdpProvider>::UdpSocket>,
244 tor_client: Arc<TorClient<R>>,
246}
247
248#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
252pub(crate) async fn bind_dns_resolver<R: Runtime>(
253 runtime: R,
254 tor_client: Arc<TorClient<R>>,
255 listen: Listen,
256) -> Result<DnsProxy<R>> {
257 if !listen.is_loopback_only() {
258 warn!(
259 "Configured to listen for DNS on non-local addresses. This is usually insecure! We recommend listening on localhost only."
260 );
261 }
262
263 let mut listeners = Vec::new();
264
265 match listen.ip_addrs() {
267 Ok(addrgroups) => {
268 for addrgroup in addrgroups {
269 let mut any_success: bool = false;
270 for addr in addrgroup {
271 match runtime.bind(&addr).await {
274 Ok(listener) => {
275 let bound_addr = listener.local_addr()?;
276 info!("Listening on {:?}.", bound_addr);
277 listeners.push(listener);
278 any_success = true;
279 }
280 #[cfg(unix)]
281 Err(ref e) if e.raw_os_error() == Some(libc::EAFNOSUPPORT) => {
282 warn_report!(e, "Address family not supported {}", addr);
283 }
284 Err(ref e) => {
285 return Err(anyhow!("Can't listen on {}: {e}", addr));
286 }
287 }
288 }
289
290 if !any_success {
291 return Err(anyhow!("All addresses failed to bind in a group"));
292 }
293 }
294 }
295 Err(e) => warn_report!(e, "Invalid listen spec"),
296 }
297 if listeners.is_empty() {
299 error!("Couldn't open any DNS listeners.");
300 return Err(anyhow!("Couldn't open any DNS listeners"));
301 }
302
303 Ok(DnsProxy {
304 tor_client,
305 udp_sockets: listeners,
306 })
307}
308
309impl<R: Runtime> DnsProxy<R> {
310 pub(crate) async fn run_dns_proxy(self) -> Result<()> {
312 let DnsProxy {
313 tor_client,
314 udp_sockets,
315 } = self;
316 run_dns_resolver_with_listeners(tor_client.runtime().clone(), tor_client, udp_sockets).await
317 }
318
319 pub(crate) fn port_info(&self) -> Result<Vec<port_info::Port>> {
321 Ok(self
322 .udp_sockets
323 .iter()
324 .map(|socket| {
325 socket.local_addr().map(|address| port_info::Port {
326 protocol: port_info::SupportedProtocol::DnsUdp,
327 address: address.into(),
328 })
329 })
330 .collect::<Result<Vec<_>, _>>()?)
331 }
332}
333
334async fn run_dns_resolver_with_listeners<R: Runtime>(
336 runtime: R,
337 tor_client: Arc<TorClient<R>>,
338 listeners: Vec<<R as tor_rtcompat::UdpProvider>::UdpSocket>,
339) -> Result<()> {
340 let mut incoming = futures::stream::select_all(
341 listeners
342 .into_iter()
343 .map(|socket| {
344 futures::stream::unfold(Arc::new(socket), |socket| async {
345 let mut packet = [0; MAX_DATAGRAM_SIZE];
346 let packet = socket
347 .recv(&mut packet)
348 .await
349 .map(|(size, remote)| (packet, size, remote, socket.clone()));
350 Some((packet, socket))
351 })
352 })
353 .enumerate()
354 .map(|(listener_id, incoming_packet)| {
355 Box::pin(incoming_packet.map(move |packet| (packet, listener_id)))
356 }),
357 );
358
359 let pending_requests = Arc::new(Mutex::new(HashMap::new()));
360 while let Some((packet, id)) = incoming.next().await {
361 let (packet, size, addr, socket) = match packet {
362 Ok(packet) => packet,
363 Err(err) => {
364 warn_report!(err, "Incoming datagram failed");
366 continue;
367 }
368 };
369
370 let client_ref = tor_client.clone();
371 runtime.spawn({
372 let pending_requests = pending_requests.clone();
373 async move {
374 let res = handle_dns_req(
375 &client_ref,
376 id,
377 &packet[..size],
378 addr,
379 socket,
380 &pending_requests,
381 )
382 .await;
383 if let Err(e) = res {
384 warn!("connection exited with error: {}", tor_error::Report(e));
386 }
387 }
388 })?;
389 }
390
391 Ok(())
392}