diff --git a/Cargo.toml b/Cargo.toml index 25a01d5..7752eee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ prctl = "1.0" smoltcp = { version = "0.10.0", features = ["std", "phy-tuntap_interface"] } socks5-impl = { version = "0.5", default-features = false } thiserror = "1.0" +trust-dns-proto = "0.22" unicase = "2.6.0" url = "2.4" diff --git a/src/dns.rs b/src/dns.rs new file mode 100644 index 0000000..f4b2404 --- /dev/null +++ b/src/dns.rs @@ -0,0 +1,104 @@ +#![allow(dead_code)] + +use std::{net::IpAddr, str::FromStr}; +use trust_dns_proto::{ + op::{Message, ResponseCode}, + rr::{record_type::RecordType, Name, RData, Record}, +}; + +#[cfg(feature = "use-rand")] +pub fn build_dns_request( + domain: &str, + query_type: RecordType, + used_by_tcp: bool, +) -> Result, String> { + // [dependencies] + // rand = "0.8" + use rand::{rngs::StdRng, Rng, SeedableRng}; + use trust_dns_proto::op::{header::MessageType, op_code::OpCode, query::Query}; + let name = Name::from_str(domain).map_err(|e| e.to_string())?; + let query = Query::query(name, query_type); + let mut msg = Message::new(); + msg.add_query(query) + .set_id(StdRng::from_entropy().gen()) + .set_op_code(OpCode::Query) + .set_message_type(MessageType::Query) + .set_recursion_desired(true); + let mut msg_buf = msg.to_vec().map_err(|e| e.to_string())?; + if used_by_tcp { + let mut buf = (msg_buf.len() as u16).to_be_bytes().to_vec(); + buf.append(&mut msg_buf); + Ok(buf) + } else { + Ok(msg_buf) + } +} + +pub fn build_dns_response( + mut request: Message, + domain: &str, + ip: IpAddr, + ttl: u32, +) -> Result { + let record = match ip { + IpAddr::V4(ip) => { + let mut record = Record::with(Name::from_str(domain)?, RecordType::A, ttl); + record.set_data(Some(RData::A(ip))); + record + } + IpAddr::V6(ip) => { + let mut record = Record::with(Name::from_str(domain)?, RecordType::AAAA, ttl); + record.set_data(Some(RData::AAAA(ip))); + record + } + }; + request.add_answer(record); + Ok(request) +} + +pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result { + if message.response_code() != ResponseCode::NoError { + return Err(format!("{:?}", message.response_code())); + } + let mut cname = None; + for answer in message.answers() { + match answer + .data() + .ok_or("DNS response not contains answer data")? + { + RData::A(addr) => { + return Ok(IpAddr::V4(*addr)); + } + RData::AAAA(addr) => { + return Ok(IpAddr::V6(*addr)); + } + RData::CNAME(name) => { + cname = Some(name.to_utf8()); + } + _ => {} + } + } + if let Some(cname) = cname { + return Err(cname); + } + Err(format!("{:?}", message.answers())) +} + +pub fn extract_domain_from_dns_message(message: &Message) -> Result { + let query = message.queries().get(0).ok_or("DnsRequest no query body")?; + let name = query.name().to_string(); + Ok(name) +} + +pub fn parse_data_to_dns_message(data: &[u8], used_by_tcp: bool) -> Result { + if used_by_tcp { + if data.len() < 2 { + return Err("invalid dns data".into()); + } + let len = u16::from_be_bytes([data[0], data[1]]) as usize; + let data = data.get(2..len + 2).ok_or("invalid dns data")?; + return parse_data_to_dns_message(data, false); + } + let message = Message::from_vec(data).map_err(|e| e.to_string())?; + Ok(message) +} diff --git a/src/lib.rs b/src/lib.rs index 98f69c6..84c3d11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ use std::{ use tun2proxy::ConnectionManager; mod android; +mod dns; pub mod error; mod http; pub mod setup; diff --git a/src/virtdns.rs b/src/virtdns.rs index 1da5c54..0d13aa6 100644 --- a/src/virtdns.rs +++ b/src/virtdns.rs @@ -8,7 +8,6 @@ use std::{ time::{Duration, Instant}, }; -const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds const MAPPING_TIMEOUT: u64 = 60; // Mapping timeout in seconds #[derive(Eq, PartialEq, Debug)] @@ -57,6 +56,23 @@ impl VirtualDns { VirtualDns::default() } + // /* + pub fn receive_query(&mut self, data: &[u8]) -> Option> { + use crate::dns; + let mut dns_block = || { + let message = dns::parse_data_to_dns_message(data, false)?; + let qname = dns::extract_domain_from_dns_message(&message)?; + if let Some(ip) = self.allocate_ip(qname.clone()) { + let message = dns::build_dns_response(message, &qname, ip, 5)?; + message.to_vec() + } else { + Err("Virtual IP space for DNS exhausted".into()) + } + }; + dns_block().ok() + } + // */ + /* pub fn receive_query(&mut self, data: &[u8]) -> Option> { if data.len() < 17 { return None; @@ -109,6 +125,8 @@ impl VirtualDns { response[8] = 0; response[9] = 0; + const DNS_TTL: u8 = 30; // TTL in DNS replies in seconds + // additional section response[10] = 0; response[11] = 0; @@ -138,7 +156,7 @@ impl VirtualDns { } Some(response) } - + // */ fn increment_ip(addr: IpAddr) -> Option { let mut ip_bytes = match addr as IpAddr { IpAddr::V4(ip) => Vec::::from(ip.octets()), @@ -239,6 +257,7 @@ impl VirtualDns { } } + /* /// Parse a non-root DNS qname at a specific offset and return the name along with its size. /// DNS packet parsing should be continued after the name. fn parse_qname(data: &[u8], mut offset: usize) -> Option<(String, usize)> { @@ -277,4 +296,5 @@ impl VirtualDns { Some((qname, offset)) } + // */ }