Implemented resolution of domain records through NS-servers. Updated dependencies.
This commit is contained in:
Generated
+14
-7
@@ -84,7 +84,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alfis"
|
||||
version = "0.6.11"
|
||||
version = "0.6.12"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
@@ -725,9 +725,9 @@ checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -759,14 +759,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
|
||||
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -1313,9 +1314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyfiledialogs"
|
||||
version = "3.9.0"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adc577626a3c26e4e1d470dbe5fe33d6fabc14e57114cb377acdb4da1a17dde9"
|
||||
checksum = "e25fa0bc43a6566e2cc6d7ac96df3fa5a57beba34445bead1b368ba8fe9ca568"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1469,6 +1470,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.78"
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alfis"
|
||||
version = "0.6.11"
|
||||
version = "0.6.12"
|
||||
authors = ["Revertron <alfis@revertron.com>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
@@ -12,7 +12,7 @@ exclude = ["blockchain.db", "alfis.toml"]
|
||||
|
||||
[dependencies]
|
||||
getopts = "0.2.21"
|
||||
log = "0.4.14"
|
||||
log = "0.4.16"
|
||||
simplelog = "0.11.2"
|
||||
toml = "0.5.8"
|
||||
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
|
||||
sqlite = "0.26.0"
|
||||
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 }
|
||||
lru = "0.7.3"
|
||||
derive_more = "0.99.17"
|
||||
@@ -45,7 +45,7 @@ lazy_static = "1.4.0"
|
||||
|
||||
# Optional dependencies regulated by features
|
||||
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 }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
+139
-58
@@ -1,3 +1,4 @@
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
@@ -7,6 +8,10 @@ use crate::blockchain::transaction::DomainData;
|
||||
use crate::dns::filter::DnsFilter;
|
||||
use crate::dns::protocol::{DnsPacket, DnsQuestion, DnsRecord, QueryType, ResultCode, TransientTtl};
|
||||
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 {
|
||||
context: Arc<Mutex<Context>>
|
||||
@@ -16,14 +21,128 @@ impl BlockchainFilter {
|
||||
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
||||
BlockchainFilter { context }
|
||||
}
|
||||
}
|
||||
|
||||
const NAME_SERVER: &str = "ns.alfis.name";
|
||||
const SERVER_ADMIN: &str = "admin.alfis.name";
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket> {
|
||||
let search;
|
||||
let top_domain;
|
||||
let subdomain;
|
||||
let parts: Vec<&str> = qname.rsplitn(3, '.').collect();
|
||||
match parts.len() {
|
||||
@@ -36,22 +155,22 @@ impl DnsFilter for BlockchainFilter {
|
||||
return None;
|
||||
}
|
||||
2 => {
|
||||
search = format!("{}.{}", parts[1], parts[0]);
|
||||
top_domain = format!("{}.{}", parts[1], parts[0]);
|
||||
subdomain = String::new();
|
||||
}
|
||||
_ => {
|
||||
search = format!("{}.{}", parts[1], parts[0]);
|
||||
top_domain = format!("{}.{}", parts[1], parts[0]);
|
||||
subdomain = String::from(parts[2]);
|
||||
}
|
||||
}
|
||||
//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();
|
||||
match data {
|
||||
None => {
|
||||
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
|
||||
let mut packet = DnsPacket::new();
|
||||
packet.questions.push(DnsQuestion::new(String::from(qname), qtype));
|
||||
@@ -64,13 +183,20 @@ impl DnsFilter for BlockchainFilter {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
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 a_record = qtype == QueryType::A || qtype == QueryType::AAAA;
|
||||
for mut record in data.records.iter_mut() {
|
||||
@@ -93,7 +219,7 @@ impl DnsFilter for BlockchainFilter {
|
||||
match record.get_domain() {
|
||||
None => {}
|
||||
Some(domain) => {
|
||||
if domain == search {
|
||||
if domain == top_domain {
|
||||
answers.push(record.clone());
|
||||
} else if domain == subdomain {
|
||||
match &mut record {
|
||||
@@ -117,13 +243,13 @@ impl DnsFilter for BlockchainFilter {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if record.get_querytype() == qtype {
|
||||
match record.get_domain() {
|
||||
None => {}
|
||||
Some(domain) => {
|
||||
if domain == search {
|
||||
if domain == top_domain {
|
||||
answers.push(record.clone());
|
||||
} else if domain == "*" {
|
||||
match &mut record {
|
||||
@@ -149,28 +275,7 @@ impl DnsFilter for BlockchainFilter {
|
||||
}
|
||||
|
||||
//debug!("Answers: {:?}", &answers);
|
||||
return 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)
|
||||
};
|
||||
return self.create_packet(qname, qtype, zone, answers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
mod tests {
|
||||
// TODO write tests for this filter
|
||||
|
||||
@@ -442,6 +442,8 @@ impl HttpsDnsClient {
|
||||
}
|
||||
dns_client.stop();
|
||||
|
||||
result.sort();
|
||||
result.dedup();
|
||||
let addrs = result
|
||||
.into_iter()
|
||||
.map(|ip| SocketAddr::new(ip, 443))
|
||||
|
||||
@@ -49,7 +49,7 @@ function refreshRecordsList() {
|
||||
var data = value.addr;
|
||||
if (value.type == "MX") {
|
||||
data = value.priority + " " + value.host;
|
||||
} else if (value.type == "CNAME") {
|
||||
} else if (value.type == "CNAME" || value.type == "NS") {
|
||||
data = value.host;
|
||||
} else if (value.type == "TXT") {
|
||||
data = value.data;
|
||||
|
||||
Reference in New Issue
Block a user