Implemented resolution of domain records through NS-servers. Updated dependencies.

This commit is contained in:
Revertron
2022-04-01 13:03:32 +02:00
parent fe43117a03
commit 594dabcab8
5 changed files with 160 additions and 70 deletions
Generated
+14 -7
View File
@@ -84,7 +84,7 @@ dependencies = [
[[package]] [[package]]
name = "alfis" name = "alfis"
version = "0.6.11" version = "0.6.12"
dependencies = [ dependencies = [
"base64", "base64",
"bincode", "bincode",
@@ -725,9 +725,9 @@ checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@@ -759,14 +759,15 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"miow", "miow",
"ntapi", "ntapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"winapi", "winapi",
] ]
@@ -1313,9 +1314,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyfiledialogs" name = "tinyfiledialogs"
version = "3.9.0" version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc577626a3c26e4e1d470dbe5fe33d6fabc14e57114cb377acdb4da1a17dde9" checksum = "e25fa0bc43a6566e2cc6d7ac96df3fa5a57beba34445bead1b368ba8fe9ca568"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -1469,6 +1470,12 @@ version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.78" version = "0.2.78"
+4 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "alfis" name = "alfis"
version = "0.6.11" version = "0.6.12"
authors = ["Revertron <alfis@revertron.com>"] authors = ["Revertron <alfis@revertron.com>"]
edition = "2021" edition = "2021"
build = "build.rs" build = "build.rs"
@@ -12,7 +12,7 @@ exclude = ["blockchain.db", "alfis.toml"]
[dependencies] [dependencies]
getopts = "0.2.21" getopts = "0.2.21"
log = "0.4.14" log = "0.4.16"
simplelog = "0.11.2" simplelog = "0.11.2"
toml = "0.5.8" toml = "0.5.8"
digest = "0.10.2" digest = "0.10.2"
@@ -37,7 +37,7 @@ rand = { version = "0.8.5", package = "rand" }
rand-old = { package = "rand", version = "0.7.0" } # For ed25519-dalek rand-old = { package = "rand", version = "0.7.0" } # For ed25519-dalek
sqlite = "0.26.0" sqlite = "0.26.0"
uuid = { version = "0.8.2", features = ["serde", "v4"] } uuid = { version = "0.8.2", features = ["serde", "v4"] }
mio = { version = "0.8.0", features = ["os-poll", "net"] } mio = { version = "0.8.2", features = ["os-poll", "net"] }
ureq = { version = "2.4", optional = true } ureq = { version = "2.4", optional = true }
lru = "0.7.3" lru = "0.7.3"
derive_more = "0.99.17" derive_more = "0.99.17"
@@ -45,7 +45,7 @@ lazy_static = "1.4.0"
# Optional dependencies regulated by features # Optional dependencies regulated by features
web-view = { version = "0.7.3", features = [], optional = true } web-view = { version = "0.7.3", features = [], optional = true }
tinyfiledialogs = { version = "3.9", optional = true } tinyfiledialogs = { version = "3.9.1", optional = true }
open = { version = "2.1.1", optional = true } open = { version = "2.1.1", optional = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
+139 -58
View File
@@ -1,3 +1,4 @@
use std::net::{IpAddr, SocketAddr};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
#[allow(unused_imports)] #[allow(unused_imports)]
@@ -7,6 +8,10 @@ use crate::blockchain::transaction::DomainData;
use crate::dns::filter::DnsFilter; use crate::dns::filter::DnsFilter;
use crate::dns::protocol::{DnsPacket, DnsQuestion, DnsRecord, QueryType, ResultCode, TransientTtl}; use crate::dns::protocol::{DnsPacket, DnsQuestion, DnsRecord, QueryType, ResultCode, TransientTtl};
use crate::Context; use crate::Context;
use crate::dns::client::{DnsClient, DnsNetworkClient};
const NAME_SERVER: &str = "ns.alfis.name";
const SERVER_ADMIN: &str = "admin.alfis.name";
pub struct BlockchainFilter { pub struct BlockchainFilter {
context: Arc<Mutex<Context>> context: Arc<Mutex<Context>>
@@ -16,14 +21,128 @@ impl BlockchainFilter {
pub fn new(context: Arc<Mutex<Context>>) -> Self { pub fn new(context: Arc<Mutex<Context>>) -> Self {
BlockchainFilter { context } BlockchainFilter { context }
} }
}
const NAME_SERVER: &str = "ns.alfis.name"; fn add_soa_record(zone: String, serial: u32, packet: &mut DnsPacket) {
const SERVER_ADMIN: &str = "admin.alfis.name"; packet.authorities.push(DnsRecord::SOA {
domain: zone,
m_name: String::from(NAME_SERVER),
r_name: String::from(SERVER_ADMIN),
serial,
refresh: 3600,
retry: 300,
expire: 604800,
minimum: 60,
ttl: TransientTtl(60)
});
}
fn get_zone_response(&self, zone: &str, serial: u32, packet: &mut DnsPacket) -> bool {
let have_zone = self.context.lock().unwrap().chain.is_available_zone(zone);
if have_zone {
BlockchainFilter::add_soa_record(zone.to_owned(), serial, packet);
}
have_zone
}
fn lookup_from_ns(qname: &str, qtype: QueryType, servers: &Vec<IpAddr>) -> Option<DnsPacket> {
let port = 10000 + (rand::random::<u16>() % 50000);
let mut dns_client = DnsNetworkClient::new(port);
dns_client.run().unwrap();
for server in servers {
let addr = SocketAddr::new(server.to_owned(), 53);
if let Ok(res) = dns_client.send_udp_query(qname, qtype, addr, false) {
dns_client.stop();
return Some(res);
}
}
dns_client.stop();
None
}
fn create_packet(&self, qname: &str, qtype: QueryType, zone: String, answers: Vec<DnsRecord>) -> Option<DnsPacket> {
if !answers.is_empty() {
// Create DnsPacket
let mut packet = DnsPacket::new();
packet.header.authoritative_answer = true;
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
for answer in answers {
packet.answers.push(answer);
}
packet.authorities.push(DnsRecord::NS { domain: zone, host: String::from(NAME_SERVER), ttl: TransientTtl(600) });
//trace!("Returning packet: {:?}", &packet);
Some(packet)
} else {
// Create DnsPacket
let mut packet = DnsPacket::new();
packet.header.authoritative_answer = true;
packet.header.rescode = ResultCode::NOERROR;
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
let serial = self.context.lock().unwrap().chain.get_soa_serial();
BlockchainFilter::add_soa_record(zone, serial, &mut packet);
//trace!("Returning packet: {:?}", &packet);
Some(packet)
}
}
fn resolve_by_ns(qname: &str, qtype: QueryType, top_domain: &String, data: &DomainData) -> (bool, Option<DnsPacket>) {
// First we search for NS records, collecting nameserver domains
let mut hosts = Vec::new();
for record in data.records.iter() {
if record.get_querytype() == QueryType::NS {
match &record {
DnsRecord::NS { domain, host, .. } if domain == "@" => {
hosts.push(host.to_owned());
}
_ => ()
}
}
}
if hosts.is_empty() {
return (false, None);
}
// Searching glue records
let mut servers = Vec::new();
for record in data.records.iter() {
match &record {
DnsRecord::A { domain, addr, .. } => {
let domain = format!("{}.{}", &domain, &top_domain);
for host in &hosts {
if &domain == host {
servers.push(IpAddr::from(addr.clone()));
}
}
}
DnsRecord::AAAA { domain, addr, .. } => {
let domain = format!("{}.{}", &domain, &top_domain);
for host in &hosts {
if &domain == host {
servers.push(IpAddr::from(addr.clone()));
}
}
}
_ => ()
}
}
if !servers.is_empty() {
trace!("Found NS servers for domain {}: {:?}", &qname, &servers);
let answer = BlockchainFilter::lookup_from_ns(qname, qtype, &servers);
if let Some(packet) = &answer {
trace!("Resolved {:?} from NS: {:?}", (qname, qtype), &packet.answers);
}
return (true, answer);
}
(false, None)
}
}
impl DnsFilter for BlockchainFilter { impl DnsFilter for BlockchainFilter {
fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket> { fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket> {
let search; let top_domain;
let subdomain; let subdomain;
let parts: Vec<&str> = qname.rsplitn(3, '.').collect(); let parts: Vec<&str> = qname.rsplitn(3, '.').collect();
match parts.len() { match parts.len() {
@@ -36,22 +155,22 @@ impl DnsFilter for BlockchainFilter {
return None; return None;
} }
2 => { 2 => {
search = format!("{}.{}", parts[1], parts[0]); top_domain = format!("{}.{}", parts[1], parts[0]);
subdomain = String::new(); subdomain = String::new();
} }
_ => { _ => {
search = format!("{}.{}", parts[1], parts[0]); top_domain = format!("{}.{}", parts[1], parts[0]);
subdomain = String::from(parts[2]); subdomain = String::from(parts[2]);
} }
} }
//trace!("Searching record type '{:?}', name '{}' for domain '{}'", &qtype, &subdomain, &search); //trace!("Searching record type '{:?}', name '{}' for domain '{}'", &qtype, &subdomain, &search);
let data = self.context.lock().unwrap().chain.get_domain_info(&search); let data = self.context.lock().unwrap().chain.get_domain_info(&top_domain);
let zone = parts[0].to_owned(); let zone = parts[0].to_owned();
match data { match data {
None => { None => {
if self.context.lock().unwrap().chain.is_available_zone(&zone) { if self.context.lock().unwrap().chain.is_available_zone(&zone) {
trace!("Not found data for domain {}", &search); trace!("Not found data for domain {}", &top_domain);
// Create DnsPacket // Create DnsPacket
let mut packet = DnsPacket::new(); let mut packet = DnsPacket::new();
packet.questions.push(DnsQuestion::new(String::from(qname), qtype)); packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
@@ -64,13 +183,20 @@ impl DnsFilter for BlockchainFilter {
} }
} }
Some(data) => { Some(data) => {
trace!("Found data for domain {}", &search); trace!("Found data for domain {}", &top_domain);
let mut data: DomainData = match serde_json::from_str(&data) { let mut data: DomainData = match serde_json::from_str(&data) {
Err(_) => { Err(_) => {
return None; return None;
} }
Ok(data) => data Ok(data) => data
}; };
// Check if this domain has NS records and needs to resolve all records through them
let (has_ns, result) = Self::resolve_by_ns(qname, qtype, &top_domain, &data);
if has_ns {
return result;
}
let mut answers: Vec<DnsRecord> = Vec::new(); let mut answers: Vec<DnsRecord> = Vec::new();
let a_record = qtype == QueryType::A || qtype == QueryType::AAAA; let a_record = qtype == QueryType::A || qtype == QueryType::AAAA;
for mut record in data.records.iter_mut() { for mut record in data.records.iter_mut() {
@@ -93,7 +219,7 @@ impl DnsFilter for BlockchainFilter {
match record.get_domain() { match record.get_domain() {
None => {} None => {}
Some(domain) => { Some(domain) => {
if domain == search { if domain == top_domain {
answers.push(record.clone()); answers.push(record.clone());
} else if domain == subdomain { } else if domain == subdomain {
match &mut record { match &mut record {
@@ -117,13 +243,13 @@ impl DnsFilter for BlockchainFilter {
} }
} }
if answers.is_empty() { if answers.is_empty() {
// If there are no records found we search for *.domain.ltd record // If there are no records found we search for *.domain.tld record
for mut record in data.records { for mut record in data.records {
if record.get_querytype() == qtype { if record.get_querytype() == qtype {
match record.get_domain() { match record.get_domain() {
None => {} None => {}
Some(domain) => { Some(domain) => {
if domain == search { if domain == top_domain {
answers.push(record.clone()); answers.push(record.clone());
} else if domain == "*" { } else if domain == "*" {
match &mut record { match &mut record {
@@ -149,28 +275,7 @@ impl DnsFilter for BlockchainFilter {
} }
//debug!("Answers: {:?}", &answers); //debug!("Answers: {:?}", &answers);
return if !answers.is_empty() { return self.create_packet(qname, qtype, zone, answers);
// Create DnsPacket
let mut packet = DnsPacket::new();
packet.header.authoritative_answer = true;
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
for answer in answers {
packet.answers.push(answer);
}
packet.authorities.push(DnsRecord::NS { domain: zone, host: String::from(NAME_SERVER), ttl: TransientTtl(600) });
//trace!("Returning packet: {:?}", &packet);
Some(packet)
} else {
// Create DnsPacket
let mut packet = DnsPacket::new();
packet.header.authoritative_answer = true;
packet.header.rescode = ResultCode::NOERROR;
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
let serial = self.context.lock().unwrap().chain.get_soa_serial();
BlockchainFilter::add_soa_record(zone, serial, &mut packet);
//trace!("Returning packet: {:?}", &packet);
Some(packet)
};
} }
} }
@@ -178,30 +283,6 @@ impl DnsFilter for BlockchainFilter {
} }
} }
impl BlockchainFilter {
fn add_soa_record(zone: String, serial: u32, packet: &mut DnsPacket) {
packet.authorities.push(DnsRecord::SOA {
domain: zone,
m_name: String::from(NAME_SERVER),
r_name: String::from(SERVER_ADMIN),
serial,
refresh: 3600,
retry: 300,
expire: 604800,
minimum: 60,
ttl: TransientTtl(60)
});
}
fn get_zone_response(&self, zone: &str, serial: u32, packet: &mut DnsPacket) -> bool {
let have_zone = self.context.lock().unwrap().chain.is_available_zone(zone);
if have_zone {
BlockchainFilter::add_soa_record(zone.to_owned(), serial, packet);
}
have_zone
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
// TODO write tests for this filter // TODO write tests for this filter
+2
View File
@@ -442,6 +442,8 @@ impl HttpsDnsClient {
} }
dns_client.stop(); dns_client.stop();
result.sort();
result.dedup();
let addrs = result let addrs = result
.into_iter() .into_iter()
.map(|ip| SocketAddr::new(ip, 443)) .map(|ip| SocketAddr::new(ip, 443))
+1 -1
View File
@@ -49,7 +49,7 @@ function refreshRecordsList() {
var data = value.addr; var data = value.addr;
if (value.type == "MX") { if (value.type == "MX") {
data = value.priority + " " + value.host; data = value.priority + " " + value.host;
} else if (value.type == "CNAME") { } else if (value.type == "CNAME" || value.type == "NS") {
data = value.host; data = value.host;
} else if (value.type == "TXT") { } else if (value.type == "TXT") {
data = value.data; data = value.data;