1
//! Implement a simple DNS resolver that relay request over Tor.
2
//!
3
//! A resolver is created with [`bind_dns_resolver()`], which opens a set of listener ports.
4
//! `DnsProxy::run_dns_proxy` then listens for
5
//! DNS requests, and sends back replies in response.
6

            
7
use futures::lock::Mutex;
8
use futures::stream::StreamExt;
9
use hickory_proto::op::{Message, OpCode, Query, ResponseCode};
10
use hickory_proto::rr::{DNSClass, Name, RData, Record, RecordType, rdata};
11
use hickory_proto::serialize::binary::{BinDecodable, BinEncodable};
12
use std::collections::HashMap;
13
use std::net::{IpAddr, SocketAddr};
14
use std::sync::Arc;
15
use tor_rtcompat::{SpawnExt, UdpProvider};
16
use tracing::{debug, error, info, warn};
17

            
18
use arti_client::{Error, HasKind, StreamPrefs, TorClient};
19
use safelog::sensitive as sv;
20
use tor_config::Listen;
21
use tor_error::{error_report, warn_report};
22
use tor_rtcompat::{Runtime, UdpSocket};
23

            
24
use anyhow::{Result, anyhow};
25

            
26
use crate::proxy::port_info;
27

            
28
/// Maximum length for receiving a single datagram
29
const MAX_DATAGRAM_SIZE: usize = 1536;
30

            
31
/// A Key used to isolate dns requests.
32
///
33
/// Composed of an usize (representing which listener socket accepted
34
/// the connection and the source IpAddr of the client)
35
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36
struct DnsIsolationKey(usize, IpAddr);
37

            
38
impl 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
/// Identifier for a DNS request, composed of its source IP and transaction ID
57
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58
struct DnsCacheKey(DnsIsolationKey, Vec<Query>);
59

            
60
/// Target for a DNS response
61
#[derive(Debug, Clone)]
62
struct DnsResponseTarget<U> {
63
    /// Transaction ID
64
    id: u16,
65
    /// Address of the client
66
    addr: SocketAddr,
67
    /// Socket to send the response through
68
    socket: Arc<U>,
69
}
70

            
71
/// Run a DNS query over tor, returning either a list of answers, or a DNS error code.
72
async fn do_query<R>(
73
    tor_client: &TorClient<R>,
74
    query: &Query,
75
    prefs: &StreamPrefs,
76
) -> Result<Vec<Record>, ResponseCode>
77
where
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
            // NoError without any body is considered to be NODATA as per rfc2308 section-2.2
85
            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 would be "torproject.org." without this
100
                    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

            
151
/// Given a datagram containing a DNS query, resolve the query over
152
/// the Tor network and send the response back.
153
async 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<()>
161
where
162
    R: Runtime,
163
    U: UdpSocket,
164
{
165
    // if we can't parse the request, don't try to answer it.
166
    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
    // According to rfc9619 there should only be 1 query per request. This constraint would
192
    // need to be relaxed for things like DNS cookies.
193
    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
                // TODO maybe add some edns?
206
                response
207
            }
208
            Err(error_type) => Message::error_msg(id, OpCode::Query, error_type),
209
        };
210
    }
211

            
212
    // remove() should never return None, but just in case
213
    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
        // ignore errors, we want to reply to everybody
222
        let response = match response.to_bytes() {
223
            Ok(r) => r,
224
            Err(e) => {
225
                // The response message probably contains the query DNS name, and the error
226
                // might well do so too.  (Many variants of hickory_proto's ProtoErrorKind
227
                // contain domain names.)  Digging into these to be more useful is tiresome,
228
                // so just mark the whole response message, and error, as sensitive.
229
                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
/// A DNS proxy server that can run indefinitely.
239
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
240
#[must_use]
241
pub(crate) struct DnsProxy<R: Runtime> {
242
    /// A list of bound UDP sockets.
243
    udp_sockets: Vec<<R as UdpProvider>::UdpSocket>,
244
    /// A tor client to handle DNS requests.
245
    tor_client: Arc<TorClient<R>>,
246
}
247

            
248
/// Bind to a set of DNS ports, and return a new DnsProxy.
249
///
250
/// Takes no action until `run_dns_proxy` is called.
251
#[cfg_attr(feature = "experimental-api", visibility::make(pub))]
252
pub(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
    // Try to bind to the DNS ports.
266
    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
                    // NOTE: Our logs here displays the local address. We allow this, since
272
                    // knowing the address is basically essential for diagnostics.
273
                    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
    // We weren't able to bind any ports: There's nothing to do.
298
    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

            
309
impl<R: Runtime> DnsProxy<R> {
310
    /// Run indefinitely, receiving incoming DNS requests and processing them.
311
    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
    /// Return a list of the port addresses that we have bound.
320
    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

            
334
/// Inner task: Receive incoming DNS requests and process them.
335
async 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
                // TODO move crate::socks::accept_err_is_fatal somewhere else and use it here?
365
                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
                    // TODO: warn_report does not work on anyhow::Error.
385
                    warn!("connection exited with error: {}", tor_error::Report(e));
386
                }
387
            }
388
        })?;
389
    }
390

            
391
    Ok(())
392
}