1
#![warn(clippy::missing_docs_in_private_items)]
2
//! # dns-resolver
3
//! Use Tor to make a DNS over TCP request for a hostname, and get IP addresses back
4
//!
5
//! ### Intro
6
//! This is a project intended to illustrate how Arti can be used to tunnel
7
//! arbitrary TCP traffic. Here, a DNS client implementation has been hand crafted
8
//! to illustrate custom made protocols being able to be used seamlessly over Tor
9
//!
10
//! ### Usage
11
//! Simply run the program:
12
//! `cargo run <hostname-to-look-up>`
13
//!
14
//! The program will then attempt to create a new Tor connection, craft the DNS
15
//! query, and send it to a DNS server (right now, Cloudflare's 1.1.1.1)
16
//!
17
//! The response is then decoded into a struct and pretty printed to the user
18
//!
19
//! ### Note on DNS
20
//! The DNS implementation showcased is not really meant for production. It is just
21
//! a quick series of hacks to show you how, if you do have a very custom protocol
22
//! that you need tunnelled over Tor, to use that protocol with Arti. For actually
23
//! tunneling DNS requests over Tor, it is recommended to use a more tried-and-tested
24
//! crate.
25
//!
26
//! For more information on DNS, you can read [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035)
27
//! or [this educational guide](https://mislove.org/teaching/cs4700/spring11/handouts/project1-primer.pdf)
28
use crate::dns::{AsBytes, FromBytes, Response};
29
use arti_client::{TorClient, TorClientConfig};
30
use std::env;
31
use tokio::io::{AsyncReadExt, AsyncWriteExt};
32
use tracing::debug;
33

            
34
mod dns;
35

            
36
#[tokio::main]
37
async fn main() {
38
    // Start logging messages
39
    tracing_subscriber::fmt::init();
40
    // Get and check CLI arguments
41
    let args: Vec<String> = env::args().collect();
42
    if args.len() != 2 {
43
        eprintln!("Usage: dns-resolver <hostname-to-lookup>");
44
        return;
45
    }
46
    // Create the default TorClientConfig and create a TorClient
47
    let config = TorClientConfig::default();
48
    let tor_client = TorClient::create_bootstrapped(config).await.unwrap();
49
    debug!("Connecting to 1.1.1.1 port 53 for DNS over TCP lookup");
50
    let mut stream = tor_client.connect(crate::dns::DNS_SERVER).await.unwrap();
51
    // We now have a TcpStream analogue to use
52
    match crate::dns::build_query(args[1].as_str()) {
53
        Ok(query) => {
54
            let req = query.as_bytes(); // Get raw bytes representation
55
            stream.write_all(req.as_slice()).await.unwrap();
56
            // Flushing ensures we actually send data over network right then instead
57
            // of waiting for buffer to fill up
58
            stream.flush().await.unwrap();
59
            debug!("Awaiting response...");
60
            let mut buf: Vec<u8> = Vec::new();
61
            // Read the response
62
            stream.read_to_end(&mut buf).await.unwrap();
63
            // Interpret the response
64
            match Response::from_bytes(&buf) {
65
                Ok(resp) => println!("{}", resp),
66
                Err(_) => eprintln!("No valid response!"),
67
            };
68
        }
69
        Err(_) => tracing::error!("Invalid domain name entered!"),
70
    };
71
}