Commit 98451760 authored by David Goulet's avatar David Goulet
Browse files

dns: DnsManager working



Signed-off-by: default avatarDavid Goulet <dgoulet@ev0ke.net>
parent 12c294c7
Loading
Loading
Loading
Loading
+12 −15
Original line number Diff line number Diff line
@@ -577,13 +577,15 @@ dependencies = [
]

[[package]]
name = "dns-parser"
version = "0.8.0"
name = "dns-message-parser"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea"
checksum = "b393503f9263e08ba3efe0a3578cdde90d6da5ae1af232cc338640670451c992"
dependencies = [
 "byteorder",
 "quick-error",
 "base64",
 "bytes",
 "hex",
 "thiserror",
]

[[package]]
@@ -815,7 +817,7 @@ dependencies = [
 "cfg-if",
 "js-sys",
 "libc",
 "wasi 0.10.2+wasi-snapshot-preview1",
 "wasi 0.10.0+wasi-snapshot-preview1",
 "wasm-bindgen",
]

@@ -1229,7 +1231,8 @@ version = "0.1.0"
dependencies = [
 "arti-client",
 "bytes",
 "dns-parser",
 "dns-message-parser",
 "futures",
 "log",
 "simple_logger",
 "smoltcp",
@@ -1442,12 +1445,6 @@ dependencies = [
 "unicode-xid",
]

[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"

[[package]]
name = "quickcheck"
version = "1.0.3"
@@ -2507,9 +2504,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"

[[package]]
name = "wasi"
+2 −1
Original line number Diff line number Diff line
@@ -5,7 +5,8 @@ edition = "2021"

[dependencies]
bytes = "~1.1.0"
dns-parser = "0.8"
dns-message-parser = "~0.6.0"
futures = "0.3"
log = "0.4"
simple_logger = "1"
smoltcp = { version = "0.8.0", default-features = false, features = ["log", "phy-tuntap_interface", "phy-raw_socket", "medium-ip", "socket-udp", "socket-tcp", "proto-ipv4", "proto-ipv6", "socket", "async"], git = "https://github.com/dgoulet-tor/smoltcp.git", rev = "8cb96670e672dfe6f7ef271ef1a3648734f94bfc" }
+74 −0
Original line number Diff line number Diff line
use futures::stream::StreamExt;
use log::{error, info};
use smoltcp::{
    socket::{UdpPacketMetadata, UdpSocketBuffer},
    wire::IpAddress,
};

use crate::socket::UdpSocket;

pub struct DnsManager {
    listener_udp_v4: UdpSocket,
}

impl DnsManager {
    pub fn new(iface: crate::socket::IFace) -> Self {
        let rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 4096]);
        let tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 4096]);

        let socket = smoltcp::socket::UdpSocket::new(rx_buffer, tx_buffer);

        let mut listener_udp_v4 = UdpSocket::new(iface, socket);
        listener_udp_v4.bind(IpAddress::v4(10, 42, 42, 53), 53);

        Self { listener_udp_v4 }
    }

    pub async fn start(&mut self) {
        info!("Starting DNS manager");

        loop {
            tokio::select! {
                r = self.listener_udp_v4.next() => match r {
                    Some((payload, endpoint)) => {
                        let ret = dns_message_parser::Dns::decode(payload);
                        match ret {
                            Ok(p) => {
                                let q = p.questions.get(0).unwrap();
                                info!("DNS Question: {} from {}", q, endpoint);
                                let a = dns_message_parser::rr::A {
                                    domain_name: q.domain_name.clone(),
                                    ttl: 10,
                                    ipv4_addr: "10.42.42.254".parse().unwrap(),
                                };
                                let rr = dns_message_parser::rr::RR::A(a);
                                let flags = dns_message_parser::Flags {
                                    qr: true,
                                    opcode: dns_message_parser::Opcode::Query,
                                    aa: true,
                                    tc: false,
                                    rd: true,
                                    ra: false,
                                    ad: false,
                                    cd: false,
                                    rcode: dns_message_parser::RCode::NoError,
                                };
                                let reply = dns_message_parser::Dns {
                                    id: p.id,
                                    flags,
                                    questions: Vec::new(),
                                    answers: vec![rr],
                                    authorities: Vec::new(),
                                    additionals: Vec::new(),
                                };
                                let _ = self.listener_udp_v4.send(reply.encode().unwrap().as_ref(), endpoint);
                            }
                            Err(e) => error!("Unable to parse DNS request: {}", e),
                        }
                    }
                    None => break,
                }
            }
        }
    }
}
+10 −0
Original line number Diff line number Diff line
mod device;
mod dns;
mod parser;
mod proxy;
mod socket;

use arti_client::{TorClient, TorClientConfig};
use device::VirtualDevice;
use dns::DnsManager;
use log::{debug, info};
use proxy::ArtiProxy;
use smoltcp::{
@@ -72,9 +74,17 @@ impl OnionTunnel {
        tokio::spawn(async move { proxy.start().await });
    }

    fn dns(&mut self) {
        let mut dns_manager = DnsManager::new(self.iface.clone());
        tokio::spawn(async move { dns_manager.start().await });
    }

    pub async fn start(&mut self) {
        info!("Starting onion tunnel on TUN iface {}", self.iface_name);

        // Spawn the DNS manager service.
        self.dns();

        loop {
            let timestamp = smoltcp::time::Instant::now();

+27 −78
Original line number Diff line number Diff line
@@ -5,15 +5,18 @@ use std::{
    task::{Context, Poll},
};

use bytes::Bytes;
use futures::Stream;
use smoltcp::{
    iface::{Interface, SocketHandle},
    phy::TunTapInterface,
    wire::IpEndpoint,
};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

use crate::device::VirtualDevice;

type IFace = Arc<Mutex<Interface<'static, VirtualDevice<TunTapInterface>>>>;
pub type IFace = Arc<Mutex<Interface<'static, VirtualDevice<TunTapInterface>>>>;

pub struct TcpSocket {
    handle: SocketHandle,
@@ -39,12 +42,22 @@ impl UdpSocket {
            .get_socket::<smoltcp::socket::UdpSocket>(self.handle))
    }

    pub fn dest(&mut self) -> (IpAddr, u16) {
    pub fn bind(&mut self, addr: smoltcp::wire::IpAddress, port: u16) {
        self.with(|s| {
            let endpoint = s.endpoint();
            (endpoint.addr.into(), endpoint.port)
            s.bind((addr, port)).expect("Unable to UDP bind");
        })
    }

    pub fn send(&mut self, payload: &[u8], endpoint: IpEndpoint) {
        self.with(|s| {
            let _ = s.send_slice(payload, endpoint);
        });
        let _ = self
            .iface
            .lock()
            .unwrap()
            .poll(smoltcp::time::Instant::now());
    }
}

impl Drop for UdpSocket {
@@ -53,33 +66,17 @@ impl Drop for UdpSocket {
    }
}

impl AsyncRead for UdpSocket {
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<std::io::Result<()>> {
impl Stream for UdpSocket {
    type Item = (Bytes, IpEndpoint);

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        self.with(|s| match s.can_recv() {
            true => {
                let rbuf = s.recv();
                match rbuf {
                    Err(e) => {
                        return Poll::Ready(Err(std::io::Error::new(
                            std::io::ErrorKind::Other,
                            format!("{}", e),
                        )))
                    }
                    Ok((payload, _)) => {
                        if payload.len() > 0 {
                            buf.put_slice(payload);
                            Poll::Ready(Ok(()))
                        } else {
                            s.register_recv_waker(cx.waker());
                            Poll::Pending
                        }
                    }
                }
            true => match s.recv() {
                Err(_) => Poll::Ready(None),
                Ok((payload, endpoint)) => {
                    Poll::Ready(Some((Bytes::copy_from_slice(payload), endpoint.clone())))
                }
            },
            false => {
                s.register_recv_waker(cx.waker());
                Poll::Pending
@@ -88,53 +85,6 @@ impl AsyncRead for UdpSocket {
    }
}

impl AsyncWrite for UdpSocket {
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<std::io::Result<usize>> {
        let dest = self.dest().into();
        let p = self.with(|s| match s.can_send() {
            true => match s.send_slice(buf, dest) {
                Ok(_) => {
                    s.register_send_waker(cx.waker());
                    Poll::Pending
                }
                Err(e) => Poll::Ready(Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    format!("{}", e),
                ))),
            },
            false => {
                s.register_send_waker(cx.waker());
                Poll::Pending
            }
        });
        match p {
            Poll::Ready(_) => {
                // We need to poll in order to process the ingress packets.
                let _ = self
                    .iface
                    .lock()
                    .unwrap()
                    .poll(smoltcp::time::Instant::now());
            }
            _ => (),
        };
        p
    }

    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        Poll::Ready(Ok(()))
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        self.with(|s| s.close());
        Poll::Ready(Ok(()))
    }
}

impl TcpSocket {
    pub fn new(iface: IFace, s: smoltcp::socket::TcpSocket<'static>) -> Self {
        let handle = iface.lock().unwrap().add_socket(s);
@@ -240,8 +190,7 @@ impl AsyncWrite for TcpSocket {
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        // XXX: Maybe should be close() and then check the state?
        self.with(|s| s.abort());
        self.with(|s| s.close());
        Poll::Ready(Ok(()))
    }
}