From d135204af78abfa9d9dce59ccebea16e9ac10da6 Mon Sep 17 00:00:00 2001 From: Revertron Date: Fri, 19 Feb 2021 16:41:43 +0100 Subject: [PATCH] Implemented DNS on blockchain. Beautified a lot of code, fixed some things. --- Cargo.toml | 6 +- alfis.cfg | 9 ++- src/blockchain/blockchain.rs | 27 ++++++- src/blockchain/filter.rs | 54 ++++++++++++++ src/blockchain/mod.rs | 1 + src/blockchain/transaction.rs | 6 ++ src/context.rs | 47 +----------- src/dns/cache.rs | 60 +++------------- src/dns/client.rs | 70 ++++-------------- src/dns/context.rs | 8 ++- src/dns/filter.rs | 16 +++++ src/dns/mod.rs | 1 + src/dns/protocol.rs | 53 +++++++------- src/dns/resolve.rs | 23 +++--- src/dns/server.rs | 88 ++++++----------------- src/lib.rs | 5 +- src/main.rs | 131 ++++++++++++++++++++++++++++------ src/miner.rs | 1 - src/p2p/network.rs | 5 +- src/settings.rs | 60 ++++++++++++++++ src/simplebus.rs | 1 + src/webview/bulma.css | 8 +++ src/webview/index.html | 74 ++++++++++++++++--- src/webview/scripts.js | 80 ++++++++++++++++++++- 24 files changed, 539 insertions(+), 295 deletions(-) create mode 100644 src/blockchain/filter.rs create mode 100644 src/dns/filter.rs create mode 100644 src/settings.rs diff --git a/Cargo.toml b/Cargo.toml index cac146d..fb580eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" authors = ["Revertron "] edition = "2018" build = "build.rs" -#![windows_subsystem = "windows"] +homepage = "https://alfis.name" +repository = "https://github.com/Revertron/Alfis" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +getopts = "0.2.21" rust-crypto = "^0.2" num_cpus = "1.13.0" byteorder = "1.3.2" @@ -37,4 +39,4 @@ serde_derive = "1.0.27" [package.metadata.winres] ProductName="ALFIS" -FileDescription="Alternative Free Identity System for independent DNS and more." \ No newline at end of file +FileDescription="Alternative Free Identity System" \ No newline at end of file diff --git a/alfis.cfg b/alfis.cfg index 5a2d805..7aa6f63 100644 --- a/alfis.cfg +++ b/alfis.cfg @@ -10,5 +10,12 @@ "127.0.0.1:10000", "127.0.0.1:10001", "127.0.0.1:10002" - ] + ], + "dns": { + "port": 53, + "forwarders": [ + "1.1.1.1", + "8.8.8.8" + ] + } } \ No newline at end of file diff --git a/src/blockchain/blockchain.rs b/src/blockchain/blockchain.rs index 8737677..99cb996 100644 --- a/src/blockchain/blockchain.rs +++ b/src/blockchain/blockchain.rs @@ -1,6 +1,7 @@ use sqlite::{Connection, State, Statement}; -use crate::{Block, Bytes, Keystore, Transaction, Settings}; +use crate::{Block, Bytes, Keystore, Transaction}; +use crate::settings::Settings; const DB_NAME: &str = "blockchain.db"; @@ -175,6 +176,30 @@ impl Blockchain { true } + pub fn get_domain_info(&self, domain: &str) -> Option { + if domain.is_empty() { + return None; + } + let identity_hash = Transaction::hash_identity(domain); + + let mut statement = self.db.prepare("SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap(); + statement.bind(1, identity_hash.as_bytes()).expect("Error in bind"); + while let State::Row = statement.next().unwrap() { + let identity = Bytes::from_bytes(statement.read::>(1).unwrap().as_slice()); + let confirmation = Bytes::from_bytes(statement.read::>(2).unwrap().as_slice()); + let method = statement.read::(3).unwrap(); + let data = statement.read::(4).unwrap(); + let pub_key = Bytes::from_bytes(statement.read::>(5).unwrap().as_slice()); + let signature = Bytes::from_bytes(statement.read::>(6).unwrap().as_slice()); + let transaction = Transaction { identity, confirmation, method, data, pub_key, signature }; + println!("Got transaction: {:?}", &transaction); + if transaction.check_for(domain) { + return Some(transaction.data); + } + } + None + } + pub fn last_block(&self) -> Option { self.last_block.clone() } diff --git a/src/blockchain/filter.rs b/src/blockchain/filter.rs new file mode 100644 index 0000000..9be95ad --- /dev/null +++ b/src/blockchain/filter.rs @@ -0,0 +1,54 @@ +use crate::Context; +use std::sync::{Mutex, Arc}; +use crate::dns::filter::DnsFilter; +use crate::dns::protocol::{DnsPacket, QueryType, DnsRecord, DnsQuestion}; + +pub struct BlockchainFilter { + context: Arc> +} + +impl BlockchainFilter { + pub fn new(context: Arc>) -> Self { + BlockchainFilter { context } + } +} + +impl DnsFilter for BlockchainFilter { + fn lookup(&self, qname: &str, qtype: QueryType) -> Option { + let data = self.context.lock().unwrap().blockchain.get_domain_info(qname); + match data { + None => { println!("Not found info for domain {}", &qname); } + Some(data) => { + let records: Vec = match serde_json::from_str(&data) { + Err(_) => { return None; } + Ok(records) => { records } + }; + let mut answers: Vec = Vec::new(); + for mut record in records { + if record.get_querytype() == qtype { + match &mut record { + // TODO make it for all types of records + DnsRecord::A { domain, .. } | DnsRecord::AAAA { domain, .. } if domain == "@" => { + *domain = String::from(qname); + } + _ => () + } + + answers.push(record); + } + } + if !answers.is_empty() { + // Create DnsPacket + let mut packet = DnsPacket::new(); + packet.questions.push(DnsQuestion::new(String::from(qname), qtype)); + for answer in answers { + packet.answers.push(answer); + } + return Some(packet); + } + } + } + + None + } +} \ No newline at end of file diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index 545461f..f330957 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -1,6 +1,7 @@ pub mod transaction; pub mod block; pub mod blockchain; +pub mod filter; pub use transaction::Transaction; pub use block::Block; diff --git a/src/blockchain/transaction.rs b/src/blockchain/transaction.rs index 1d12e43..e588e96 100644 --- a/src/blockchain/transaction.rs +++ b/src/blockchain/transaction.rs @@ -67,6 +67,12 @@ impl Transaction { digest.result(&mut buf); Bytes::from_bytes(&buf) } + + pub fn check_for(&self, domain: &str) -> bool { + let hash = Self::hash_identity(&domain); + let confirmation = Self::hash_with_key(&domain, &self.pub_key); + self.identity.eq(&hash) && self.confirmation.eq(&confirmation) + } } impl fmt::Debug for Transaction { diff --git a/src/context.rs b/src/context.rs index 035cf01..2c168d9 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,6 @@ -use crate::{Keystore, Blockchain, Bus, Bytes}; +use crate::{Blockchain, Bus, Keystore}; use crate::event::Event; -use serde::{Serialize, Deserialize}; -use std::fs::File; -use std::io::Read; -use std::sync::MutexGuard; +use crate::settings::Settings; pub struct Context { pub settings: Settings, @@ -43,44 +40,4 @@ impl Context { pub fn get_blockchain(&self) -> &Blockchain { &self.blockchain } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Settings { - pub origin: String, - pub version: u32, - pub key_file: String, - pub listen: String, - pub public: bool, - pub peers: Vec -} - -impl Settings { - pub fn new>(settings: S) -> serde_json::Result { - serde_json::from_str(&settings.into()) - } - - pub fn load(file_name: &str) -> Option { - match File::open(file_name) { - Ok(mut file) => { - let mut text = String::new(); - file.read_to_string(&mut text).unwrap(); - let loaded = serde_json::from_str(&text); - return if loaded.is_ok() { - Some(loaded.unwrap()) - } else { - None - } - }, - Err(..) => None - } - } - - pub fn get_origin(&self) -> Bytes { - if self.origin.eq("") { - return Bytes::zero32(); - } - let origin = crate::from_hex(&self.origin).expect("Wrong origin in settings"); - Bytes::from_bytes(origin.as_slice()) - } } \ No newline at end of file diff --git a/src/dns/cache.rs b/src/dns/cache.rs index 64f1332..1b2dc81 100644 --- a/src/dns/cache.rs +++ b/src/dns/cache.rs @@ -39,25 +39,15 @@ impl PartialEq for RecordEntry { } impl Hash for RecordEntry { - fn hash(&self, state: &mut H) - where - H: Hasher, - { + fn hash(&self, state: &mut H) where H: Hasher { self.record.hash(state); } } #[derive(Clone, Debug, Serialize, Deserialize)] pub enum RecordSet { - NoRecords { - qtype: QueryType, - ttl: u32, - timestamp: DateTime, - }, - Records { - qtype: QueryType, - records: HashSet, - }, + NoRecords { qtype: QueryType, ttl: u32, timestamp: DateTime }, + Records { qtype: QueryType, records: HashSet }, } #[derive(Clone, Debug)] @@ -70,22 +60,13 @@ pub struct DomainEntry { impl DomainEntry { pub fn new(domain: String) -> DomainEntry { - DomainEntry { - domain: domain, - record_types: HashMap::new(), - hits: 0, - updates: 0, - } + DomainEntry { domain, record_types: HashMap::new(), hits: 0, updates: 0 } } pub fn store_nxdomain(&mut self, qtype: QueryType, ttl: u32) { self.updates += 1; - let new_set = RecordSet::NoRecords { - qtype: qtype, - ttl: ttl, - timestamp: Local::now(), - }; + let new_set = RecordSet::NoRecords { qtype, ttl, timestamp: Local::now() }; self.record_types.insert(qtype, new_set); } @@ -93,15 +74,9 @@ impl DomainEntry { pub fn store_record(&mut self, rec: &DnsRecord) { self.updates += 1; - let entry = RecordEntry { - record: rec.clone(), - timestamp: Local::now(), - }; + let entry = RecordEntry { record: rec.clone(), timestamp: Local::now() }; - if let Some(&mut RecordSet::Records { - ref mut records, .. - }) = self.record_types.get_mut(&rec.get_querytype()) - { + if let Some(&mut RecordSet::Records { ref mut records, .. }) = self.record_types.get_mut(&rec.get_querytype()) { if records.contains(&entry) { records.remove(&entry); } @@ -113,10 +88,7 @@ impl DomainEntry { let mut records = HashSet::new(); records.insert(entry); - let new_set = RecordSet::Records { - qtype: rec.get_querytype(), - records: records, - }; + let new_set = RecordSet::Records { qtype: rec.get_querytype(), records }; self.record_types.insert(rec.get_querytype(), new_set); } @@ -191,9 +163,7 @@ pub struct Cache { impl Cache { pub fn new() -> Cache { - Cache { - domain_entries: BTreeMap::new(), - } + Cache { domain_entries: BTreeMap::new() } } fn get_cache_state(&mut self, qname: &str, qtype: QueryType) -> CacheState { @@ -203,13 +173,7 @@ impl Cache { } } - fn fill_queryresult( - &mut self, - qname: &str, - qtype: QueryType, - result_vec: &mut Vec, - increment_stats: bool, - ) { + fn fill_queryresult(&mut self,qname: &str, qtype: QueryType, result_vec: &mut Vec, increment_stats: bool) { if let Some(domain_entry) = self.domain_entries.get_mut(qname).and_then(Arc::get_mut) { if increment_stats { domain_entry.hits += 1 @@ -275,9 +239,7 @@ pub struct SynchronizedCache { impl SynchronizedCache { pub fn new() -> SynchronizedCache { - SynchronizedCache { - cache: RwLock::new(Cache::new()), - } + SynchronizedCache { cache: RwLock::new(Cache::new()) } } pub fn list(&self) -> Result>> { diff --git a/src/dns/client.rs b/src/dns/client.rs index 7603743..ff034d6 100644 --- a/src/dns/client.rs +++ b/src/dns/client.rs @@ -32,13 +32,7 @@ pub trait DnsClient { fn get_failed_count(&self) -> usize; fn run(&self) -> Result<()>; - fn send_query( - &self, - qname: &str, - qtype: QueryType, - server: (&str, u16), - recursive: bool, - ) -> Result; + fn send_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result; } /// The UDP client @@ -72,6 +66,7 @@ struct PendingQuery { } unsafe impl Send for DnsNetworkClient {} + unsafe impl Sync for DnsNetworkClient {} impl DnsNetworkClient { @@ -89,13 +84,7 @@ impl DnsNetworkClient { /// /// This is much simpler than using UDP, since the kernel will take care of /// packet ordering, connection state, timeouts etc. - pub fn send_tcp_query( - &self, - qname: &str, - qtype: QueryType, - server: (&str, u16), - recursive: bool, - ) -> Result { + pub fn send_tcp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result { let _ = self.total_sent.fetch_add(1, Ordering::Release); // Prepare request @@ -135,14 +124,8 @@ impl DnsNetworkClient { /// The query is sent from the callee thread, but responses are read on a /// worker thread, and returned to this thread through a channel. Thus this /// method is thread safe, and can be used from any number of threads in - /// parallell. - pub fn send_udp_query( - &self, - qname: &str, - qtype: QueryType, - server: (&str, u16), - recursive: bool, - ) -> Result { + /// parallel. + pub fn send_udp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result { let _ = self.total_sent.fetch_add(1, Ordering::Release); // Prepare request @@ -156,30 +139,20 @@ impl DnsNetworkClient { packet.header.questions = 1; packet.header.recursion_desired = recursive; - packet - .questions - .push(DnsQuestion::new(qname.to_string(), qtype)); + packet.questions.push(DnsQuestion::new(qname.to_string(), qtype)); // Create a return channel, and add a `PendingQuery` to the list of lookups // in progress let (tx, rx) = channel(); { - let mut pending_queries = self - .pending_queries - .lock() - .map_err(|_| ClientError::PoisonedLock)?; - pending_queries.push(PendingQuery { - seq: packet.header.id, - timestamp: Local::now(), - tx: tx, - }); + let mut pending_queries = self.pending_queries.lock().map_err(|_| ClientError::PoisonedLock)?; + pending_queries.push(PendingQuery { seq: packet.header.id, timestamp: Local::now(), tx }); } // Send query let mut req_buffer = BytePacketBuffer::new(); packet.write(&mut req_buffer, 512)?; - self.socket - .send_to(&req_buffer.buf[0..req_buffer.pos], server)?; + self.socket.send_to(&req_buffer.buf[0..req_buffer.pos], server)?; // Wait for response match rx.recv() { @@ -231,10 +204,7 @@ impl DnsClient for DnsNetworkClient { let packet = match DnsPacket::from_buffer(&mut res_buffer) { Ok(packet) => packet, Err(err) => { - println!( - "DnsNetworkClient failed to parse packet with error: {}", - err - ); + println!("DnsNetworkClient failed to parse packet with error: {:?}", err); continue; } }; @@ -298,13 +268,7 @@ impl DnsClient for DnsNetworkClient { Ok(()) } - fn send_query( - &self, - qname: &str, - qtype: QueryType, - server: (&str, u16), - recursive: bool, - ) -> Result { + fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result { let packet = self.send_udp_query(qname, qtype, server, recursive)?; if !packet.header.truncated_message { return Ok(packet); @@ -317,7 +281,6 @@ impl DnsClient for DnsNetworkClient { #[cfg(test)] pub mod tests { - use super::*; use crate::dns::protocol::{DnsPacket, DnsRecord, QueryType}; @@ -329,11 +292,12 @@ pub mod tests { impl<'a> DnsStubClient { pub fn new(callback: Box) -> DnsStubClient { - DnsStubClient { callback: callback } + DnsStubClient { callback } } } unsafe impl Send for DnsStubClient {} + unsafe impl Sync for DnsStubClient {} impl DnsClient for DnsStubClient { @@ -349,13 +313,7 @@ pub mod tests { Ok(()) } - fn send_query( - &self, - qname: &str, - qtype: QueryType, - server: (&str, u16), - recursive: bool, - ) -> Result { + fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result { (self.callback)(qname, qtype, server, recursive) } } diff --git a/src/dns/context.rs b/src/dns/context.rs index 0490c55..9e74a3d 100644 --- a/src/dns/context.rs +++ b/src/dns/context.rs @@ -10,6 +10,7 @@ use crate::dns::authority::Authority; use crate::dns::cache::SynchronizedCache; use crate::dns::client::{DnsClient, DnsNetworkClient}; use crate::dns::resolve::{DnsResolver, ForwardingDnsResolver, RecursiveDnsResolver}; +use crate::dns::filter::DnsFilter; #[derive(Debug, Display, From, Error)] pub enum ContextError { @@ -43,6 +44,7 @@ pub enum ResolveStrategy { pub struct ServerContext { pub authority: Authority, pub cache: SynchronizedCache, + pub filters: Vec>, pub client: Box, pub dns_port: u16, pub api_port: u16, @@ -66,6 +68,7 @@ impl ServerContext { ServerContext { authority: Authority::new(), cache: SynchronizedCache::new(), + filters: Vec::new(), client: Box::new(DnsNetworkClient::new(34255)), dns_port: 53, api_port: 5380, @@ -73,7 +76,7 @@ impl ServerContext { allow_recursive: true, enable_udp: true, enable_tcp: true, - enable_api: true, + enable_api: false, statistics: ServerStatistics { tcp_query_count: AtomicUsize::new(0), udp_query_count: AtomicUsize::new(0), @@ -122,6 +125,7 @@ pub mod tests { Arc::new(ServerContext { authority: Authority::new(), cache: SynchronizedCache::new(), + filters: Vec::new(), client: Box::new(DnsStubClient::new(callback)), dns_port: 53, api_port: 5380, @@ -129,7 +133,7 @@ pub mod tests { allow_recursive: true, enable_udp: true, enable_tcp: true, - enable_api: true, + enable_api: false, statistics: ServerStatistics { tcp_query_count: AtomicUsize::new(0), udp_query_count: AtomicUsize::new(0), diff --git a/src/dns/filter.rs b/src/dns/filter.rs new file mode 100644 index 0000000..26120f9 --- /dev/null +++ b/src/dns/filter.rs @@ -0,0 +1,16 @@ +use crate::dns::protocol::{QueryType, DnsPacket}; + +pub trait DnsFilter { + fn lookup(&self, qname: &str, qtype: QueryType) -> Option; +} + +pub struct DummyFilter { + +} + +#[allow(unused_variables)] +impl DnsFilter for DummyFilter { + fn lookup(&self, qname: &str, qtype: QueryType) -> Option { + None + } +} \ No newline at end of file diff --git a/src/dns/mod.rs b/src/dns/mod.rs index 1783419..e4eb8f3 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -22,5 +22,6 @@ pub mod context; pub mod protocol; pub mod resolve; pub mod server; +pub mod filter; mod netutil; diff --git a/src/dns/protocol.rs b/src/dns/protocol.rs index b22c665..4d38f47 100644 --- a/src/dns/protocol.rs +++ b/src/dns/protocol.rs @@ -188,8 +188,8 @@ impl DnsRecord { ); Ok(DnsRecord::A { - domain: domain, - addr: addr, + domain, + addr, ttl: TransientTtl(ttl), }) } @@ -210,8 +210,8 @@ impl DnsRecord { ); Ok(DnsRecord::AAAA { - domain: domain, - addr: addr, + domain, + addr, ttl: TransientTtl(ttl), }) } @@ -220,7 +220,7 @@ impl DnsRecord { buffer.read_qname(&mut ns)?; Ok(DnsRecord::NS { - domain: domain, + domain, host: ns, ttl: TransientTtl(ttl), }) @@ -230,7 +230,7 @@ impl DnsRecord { buffer.read_qname(&mut cname)?; Ok(DnsRecord::CNAME { - domain: domain, + domain, host: cname, ttl: TransientTtl(ttl), }) @@ -244,10 +244,10 @@ impl DnsRecord { buffer.read_qname(&mut srv)?; Ok(DnsRecord::SRV { - domain: domain, - priority: priority, - weight: weight, - port: port, + domain, + priority, + weight, + port, host: srv, ttl: TransientTtl(ttl), }) @@ -258,8 +258,8 @@ impl DnsRecord { buffer.read_qname(&mut mx)?; Ok(DnsRecord::MX { - domain: domain, - priority: priority, + domain, + priority, host: mx, ttl: TransientTtl(ttl), }) @@ -278,14 +278,14 @@ impl DnsRecord { let minimum = buffer.read_u32()?; Ok(DnsRecord::SOA { - domain: domain, - m_name: m_name, - r_name: r_name, - serial: serial, - refresh: refresh, - retry: retry, - expire: expire, - minimum: minimum, + domain, + m_name, + r_name, + serial, + refresh, + retry, + expire, + minimum, ttl: TransientTtl(ttl), }) } @@ -300,7 +300,7 @@ impl DnsRecord { buffer.step(data_len as usize)?; Ok(DnsRecord::TXT { - domain: domain, + domain, data: txt, ttl: TransientTtl(ttl), }) @@ -317,16 +317,16 @@ impl DnsRecord { Ok(DnsRecord::OPT { packet_len: class, flags: ttl, - data: data, + data, }) } QueryType::UNKNOWN(_) => { buffer.step(data_len as usize)?; Ok(DnsRecord::UNKNOWN { - domain: domain, + domain, qtype: qtype_num, - data_len: data_len, + data_len, ttl: TransientTtl(ttl), }) } @@ -755,10 +755,7 @@ pub struct DnsQuestion { impl DnsQuestion { pub fn new(name: String, qtype: QueryType) -> DnsQuestion { - DnsQuestion { - name: name, - qtype: qtype, - } + DnsQuestion { name, qtype } } pub fn binary_len(&self) -> usize { diff --git a/src/dns/resolve.rs b/src/dns/resolve.rs index 6bcfbc3..16e5b71 100644 --- a/src/dns/resolve.rs +++ b/src/dns/resolve.rs @@ -51,6 +51,12 @@ pub trait DnsResolver { } } + for filter in self.get_context().filters.iter() { + if let Some(packet) = filter.lookup(qname, qtype) { + return Ok(packet); + } + } + self.perform(qname, qtype) } @@ -67,10 +73,7 @@ pub struct ForwardingDnsResolver { impl ForwardingDnsResolver { pub fn new(context: Arc, server: (String, u16)) -> ForwardingDnsResolver { - ForwardingDnsResolver { - context: context, - server: server, - } + ForwardingDnsResolver { context, server } } } @@ -81,10 +84,12 @@ impl DnsResolver for ForwardingDnsResolver { fn perform(&mut self, qname: &str, qtype: QueryType) -> Result { let &(ref host, port) = &self.server; - let result = self - .context - .client - .send_query(qname, qtype, (host.as_str(), port), true)?; + let result = match self.context.cache.lookup(qname, qtype) { + None => { + self.context.client.send_query(qname, qtype, (host.as_str(), port), true)? + } + Some(packet) => packet + }; self.context.cache.store(&result.answers)?; @@ -101,7 +106,7 @@ pub struct RecursiveDnsResolver { impl RecursiveDnsResolver { pub fn new(context: Arc) -> RecursiveDnsResolver { - RecursiveDnsResolver { context: context } + RecursiveDnsResolver { context } } } diff --git a/src/dns/server.rs b/src/dns/server.rs index f7db44d..903fff1 100644 --- a/src/dns/server.rs +++ b/src/dns/server.rs @@ -59,8 +59,7 @@ pub trait DnsServer { } /// Utility function for resolving domains referenced in for example CNAME or SRV -/// records. This usually spares the client from having to perform additional -/// lookups. +/// records. This usually spares the client from having to perform additional lookups. fn resolve_cnames( lookup_list: &[DnsRecord], results: &mut Vec, @@ -112,11 +111,7 @@ pub fn execute_query(context: Arc, request: &DnsPacket) -> DnsPac packet.questions.push(question.clone()); let mut resolver = context.create_resolver(context.clone()); - let rescode = match resolver.resolve( - &question.name, - question.qtype, - request.header.recursion_desired, - ) { + let rescode = match resolver.resolve(&question.name, question.qtype, request.header.recursion_desired) { Ok(result) => { let rescode = result.header.rescode; @@ -128,10 +123,7 @@ pub fn execute_query(context: Arc, request: &DnsPacket) -> DnsPac rescode } Err(err) => { - println!( - "Failed to resolve {:?} {}: {:?}", - question.qtype, question.name, err - ); + println!("Failed to resolve {:?} {}: {:?}", question.qtype, question.name, err); ResultCode::SERVFAIL } }; @@ -169,10 +161,10 @@ pub struct DnsUdpServer { impl DnsUdpServer { pub fn new(context: Arc, thread_count: usize) -> DnsUdpServer { DnsUdpServer { - context: context, + context, request_queue: Arc::new(Mutex::new(VecDeque::new())), request_cond: Arc::new(Condvar::new()), - thread_count: thread_count, + thread_count, } } } @@ -180,11 +172,10 @@ impl DnsUdpServer { impl DnsServer for DnsUdpServer { /// Launch the server /// - /// This method takes ownership of the server, preventing the method from - /// being called multiple times. + /// This method takes ownership of the server, preventing the method from being called multiple times. fn run_server(self) -> Result<()> { // Bind the socket - let socket = UdpSocket::bind(("0.0.0.0", self.context.dns_port))?; + let socket = UdpSocket::bind(("[::]", self.context.dns_port))?; // Spawn threads for handling requests for thread_id in 0..self.thread_count { @@ -227,8 +218,7 @@ impl DnsServer for DnsUdpServer { } } - // Create a response buffer, and ask the context for an appropriate - // resolver + // Create a response buffer, and ask the context for an appropriate resolver let mut res_buffer = VectorPacketBuffer::new(); let mut packet = execute_query(context.clone(), &request); @@ -236,14 +226,8 @@ impl DnsServer for DnsUdpServer { // Fire off the response let len = res_buffer.pos(); - let data = return_or_report!( - res_buffer.get_range(0, len), - "Failed to get buffer data" - ); - ignore_or_report!( - socket_clone.send_to(data, src), - "Failed to send response packet" - ); + let data = return_or_report!(res_buffer.get_range(0, len), "Failed to get buffer data"); + ignore_or_report!(socket_clone.send_to(data, src), "Failed to send response packet"); } })?; } @@ -253,11 +237,7 @@ impl DnsServer for DnsUdpServer { .name("DnsUdpServer-incoming".into()) .spawn(move || { loop { - let _ = self - .context - .statistics - .udp_query_count - .fetch_add(1, Ordering::Release); + let _ = self.context.statistics.udp_query_count.fetch_add(1, Ordering::Release); // Read a query packet let mut req_buffer = BytePacketBuffer::new(); @@ -278,8 +258,7 @@ impl DnsServer for DnsUdpServer { } }; - // Acquire lock, add request to queue, and notify waiting threads - // using the condition. + // Acquire lock, add request to queue, and notify waiting threads using the condition. match self.request_queue.lock() { Ok(mut queue) => { queue.push_back((src, request)); @@ -305,17 +284,13 @@ pub struct DnsTcpServer { impl DnsTcpServer { pub fn new(context: Arc, thread_count: usize) -> DnsTcpServer { - DnsTcpServer { - context: context, - senders: Vec::new(), - thread_count: thread_count, - } + DnsTcpServer { context, senders: Vec::new(), thread_count } } } impl DnsServer for DnsTcpServer { fn run_server(mut self) -> Result<()> { - let socket = TcpListener::bind(("0.0.0.0", self.context.dns_port))?; + let socket = TcpListener::bind(("[::]", self.context.dns_port))?; // Spawn threads for handling requests, and create the channels for thread_id in 0..self.thread_count { @@ -332,48 +307,30 @@ impl DnsServer for DnsTcpServer { Err(_) => continue, }; - let _ = context - .statistics - .tcp_query_count - .fetch_add(1, Ordering::Release); + let _ = context.statistics.tcp_query_count.fetch_add(1, Ordering::Release); // When DNS packets are sent over TCP, they're prefixed with a two byte // length. We don't really need to know the length in advance, so we // just move past it and continue reading as usual - ignore_or_report!( - read_packet_length(&mut stream), - "Failed to read query packet length" - ); + ignore_or_report!(read_packet_length(&mut stream), "Failed to read query packet length"); let request = { let mut stream_buffer = StreamPacketBuffer::new(&mut stream); - return_or_report!( - DnsPacket::from_buffer(&mut stream_buffer), - "Failed to read query packet" - ) + return_or_report!(DnsPacket::from_buffer(&mut stream_buffer), "Failed to read query packet") }; let mut res_buffer = VectorPacketBuffer::new(); let mut packet = execute_query(context.clone(), &request); - ignore_or_report!( - packet.write(&mut res_buffer, 0xFFFF), - "Failed to write packet to buffer" - ); + ignore_or_report!(packet.write(&mut res_buffer, 0xFFFF), "Failed to write packet to buffer"); // As is the case for incoming queries, we need to send a 2 byte length // value before handing of the actual packet. let len = res_buffer.pos(); - ignore_or_report!( - write_packet_length(&mut stream, len), - "Failed to write packet size" - ); + ignore_or_report!(write_packet_length(&mut stream, len), "Failed to write packet size"); // Now we can go ahead and write the actual packet - let data = return_or_report!( - res_buffer.get_range(0, len), - "Failed to get packet data" - ); + let data = return_or_report!(res_buffer.get_range(0, len), "Failed to get packet data"); ignore_or_report!(stream.write(data), "Failed to write response packet"); @@ -399,10 +356,7 @@ impl DnsServer for DnsTcpServer { match self.senders[thread_no].send(stream) { Ok(_) => {} Err(e) => { - println!( - "Failed to send TCP request for processing on thread {}: {}", - thread_no, e - ); + println!("Failed to send TCP request for processing on thread {}: {}", thread_no, e); } } } diff --git a/src/lib.rs b/src/lib.rs index 9f55fd1..22d0a56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,13 @@ pub use blockchain::transaction::Transaction; pub use crate::blockchain::Blockchain; pub use crate::context::Context; -pub use crate::context::Settings; +pub use settings::Settings; pub use crate::keys::Bytes; pub use crate::keys::Keystore; pub use crate::simplebus::*; pub use crate::utils::*; -mod blockchain; +pub mod blockchain; pub mod utils; pub mod simplebus; pub mod keys; @@ -18,4 +18,5 @@ pub mod context; pub mod event; pub mod p2p; pub mod dns; +pub mod settings; diff --git a/src/main.rs b/src/main.rs index 7fd8605..9c43e5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,22 +2,31 @@ extern crate web_view; extern crate tinyfiledialogs as tfd; +use std::env; use std::sync::{Arc, Mutex}; -use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::thread; +use std::time::Duration; use rand::RngCore; -use serde::{Deserialize}; +use serde::Deserialize; use web_view::*; +use getopts::Options; -use alfis::{Blockchain, Bytes, Context, Keystore, Settings, Transaction, Block}; +use alfis::{Blockchain, Bytes, Context, Keystore, Transaction}; use alfis::event::Event; use alfis::miner::Miner; use alfis::p2p::Network; +use alfis::settings::Settings; +use alfis::dns::context::{ServerContext, ResolveStrategy}; +use alfis::dns::server::{DnsServer, DnsUdpServer, DnsTcpServer}; +use alfis::dns::protocol::DnsRecord; +use alfis::blockchain::filter::BlockchainFilter; extern crate serde; extern crate serde_json; +#[allow(dead_code)] const ONE_YEAR: u16 = 365; const GENESIS_ZONE: &str = "ygg"; const GENESIS_ZONE_DIFFICULTY: u16 = 20; @@ -26,6 +35,27 @@ const SETTINGS_FILENAME: &str = "alfis.cfg"; fn main() { println!("ALFIS 0.1.0"); + let args: Vec = env::args().collect(); + let program = args[0].clone(); + + let mut opts = Options::new(); + opts.optflag("h","help", "Print this help menu"); + opts.optflag("n","nogui","Run without graphic user interface"); + opts.optopt("c","config","Path to config file", ""); + + let opt_matches = match opts.parse(&args[1..]) { + Ok(m) => m, + Err(f) => panic!(f.to_string()), + }; + + if opt_matches.opt_present("h") { + let brief = format!("Usage: {} [options]", program); + print!("{}", opts.usage(&brief)); + return; + } + + let no_gui = opt_matches.opt_present("n"); + let settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings"); let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") { None => { @@ -39,7 +69,9 @@ fn main() { None => { println!("No blocks found in DB"); } Some(block) => { println!("Loaded DB with origin {:?}", &block.hash); } } + let settings_copy = settings.clone(); let context: Arc> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain))); + start_dns_server(&context, &settings_copy); let mut miner_obj = Miner::new(context.clone()); miner_obj.start_mining_thread(); @@ -49,7 +81,32 @@ fn main() { network.start().expect("Error starting network component"); create_genesis_if_needed(&context, &miner); - run_interface(context.clone(), miner.clone()); + if no_gui { + let sleep = Duration::from_millis(1000); + loop { + thread::sleep(sleep); + } + } else { + run_interface(context.clone(), miner.clone()); + } +} + +fn start_dns_server(context: &Arc>, settings: &Settings) { + let server_context = create_server_context(context.clone(), &settings); + + if server_context.enable_udp { + let udp_server = DnsUdpServer::new(server_context.clone(), 20); + if let Err(e) = udp_server.run_server() { + println!("Failed to bind UDP listener: {:?}", e); + } + } + + if server_context.enable_tcp { + let tcp_server = DnsTcpServer::new(server_context.clone(), 20); + if let Err(e) = tcp_server.run_server() { + println!("Failed to bind TCP listener: {:?}", e); + } + } } fn create_genesis_if_needed(context: &Arc>, miner: &Arc>) { @@ -85,7 +142,6 @@ fn run_interface(context: Arc>, miner: Arc>) { Loaded => { web_view.eval("showMiningIndicator(false);").expect("Error evaluating!"); let handle = web_view.handle(); - let context_copy = context.clone(); let mut c = context.lock().unwrap(); c.bus.register(move |_uuid, e| { println!("Got event from bus {:?}", &e); @@ -103,8 +159,7 @@ fn run_interface(context: Arc>, miner: Arc>) { if !eval.is_empty() { println!("Evaluating {}", &eval); handle.dispatch(move |web_view| { - web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!"); - return WVResult::Ok(()); + web_view.eval(&eval.replace("\\", "\\\\")) }).expect("Error dispatching!"); } true @@ -154,19 +209,22 @@ fn run_interface(context: Arc>, miner: Arc>) { let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore()); web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!"); } - CreateDomain { name, records, tags } => { - let keystore = { - let guard = context.lock().unwrap(); - guard.get_keystore() - }; - create_domain(miner.clone(), name, records, &keystore); + CreateDomain { name, records, .. } => { + println!("Got records: {}", records); + if serde_json::from_str::>(&records).is_ok() { + let keystore = { + let guard = context.lock().unwrap(); + guard.get_keystore() + }; + create_domain(miner.clone(), name, records, &keystore); + } else { + println!("Error in DNS records for domain!"); + web_view.eval(&format!("showWarning('{}');", "Something wrong with your records! Please, correct the error and try again.")); + } } - ChangeDomain { name, records, tags } => { - let keystore = { context.lock().unwrap().get_keystore() }; - // TODO - } - RenewDomain { name, days } => {} - TransferDomain { name, owner } => {} + ChangeDomain { .. } => {} + RenewDomain { .. } => {} + TransferDomain { .. } => {} StopMining => { context.lock().unwrap().bus.post(Event::ActionStopMining); } @@ -192,7 +250,7 @@ fn create_domain>(miner: Arc>, name: S, data: S, ke println!("Generating domain {}", name); //let rec_vector: Vec = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect(); //let tags_vector: Vec = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect(); - let transaction = { create_transaction(keystore, name, "domain".into(), data.into()) }; + let transaction = create_transaction(keystore, name, "domain".into(), data.into()); let mut miner_guard = miner.lock().unwrap(); miner_guard.add_transaction(transaction); } @@ -259,6 +317,23 @@ fn generate_key(difficulty: usize, mining: Arc) -> Option } } +fn create_server_context(context: Arc>, settings: &Settings) -> Arc { + let mut server_context = ServerContext::new(); + server_context.allow_recursive = true; + server_context.dns_port = settings.dns.port; + server_context.resolve_strategy = match settings.dns.forwarders.is_empty() { + true => { ResolveStrategy::Recursive } + false => { ResolveStrategy::Forward { host: settings.dns.forwarders[0].clone(), port: 53 }} // TODO refactor to use more resolvers + }; + server_context.filters.push(Box::new(BlockchainFilter::new(context))); + match server_context.initialize() { + Ok(_) => {} + Err(e) => { panic!("Server failed to initialize: {:?}", e); } + } + + Arc::new(server_context) +} + #[derive(Deserialize)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { @@ -281,3 +356,19 @@ fn inline_style(s: &str) -> String { fn inline_script(s: &str) -> String { format!(r#""#, s) } + +#[cfg(test)] +mod tests { + use alfis::dns::protocol::{DnsRecord, TransientTtl}; + + #[test] + fn record_to_string() { + let record = DnsRecord::A { + domain: "google.com".to_string(), + addr: "127.0.0.1".parse().unwrap(), + ttl: TransientTtl(300) + }; + println!("Record is {:?}", &record); + println!("Record in JSON is {}", serde_json::to_string(&record).unwrap()); + } +} \ No newline at end of file diff --git a/src/miner.rs b/src/miner.rs index 264a718..cf86700 100644 --- a/src/miner.rs +++ b/src/miner.rs @@ -10,7 +10,6 @@ use num_cpus; use crate::{Block, Bytes, Context, hash_is_good, Transaction}; use crate::event::Event; -use std::ops::DerefMut; pub struct Miner { context: Arc>, diff --git a/src/p2p/network.rs b/src/p2p/network.rs index 453822d..b30663f 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -13,7 +13,6 @@ use mio::net::{TcpListener, TcpStream}; use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers}; use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown}; -use std::ops::DerefMut; const SERVER: Token = Token(0); const POLL_TIMEOUT: Option = Some(Duration::from_millis(3000)); @@ -95,8 +94,8 @@ impl Network { None => {} Some(mut peer) => { let stream = peer.get_stream(); - poll.registry().deregister(stream); - stream.shutdown(Shutdown::Both); + let _ = poll.registry().deregister(stream); + let _ = stream.shutdown(Shutdown::Both); println!("Peer connection {:?} has shut down", &peer.get_addr()); } } diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..ec1d9c0 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,60 @@ +use std::fs::File; +use std::io::Read; + +use serde::{Deserialize, Serialize}; + +use crate::Bytes; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Settings { + pub origin: String, + pub version: u32, + pub key_file: String, + pub listen: String, + pub public: bool, + pub peers: Vec, + #[serde(default)] + pub dns: Dns +} + +impl Settings { + pub fn new>(settings: S) -> serde_json::Result { + serde_json::from_str(&settings.into()) + } + + pub fn load(file_name: &str) -> Option { + match File::open(file_name) { + Ok(mut file) => { + let mut text = String::new(); + file.read_to_string(&mut text).unwrap(); + let loaded = serde_json::from_str(&text); + return if loaded.is_ok() { + Some(loaded.unwrap()) + } else { + None + } + }, + Err(..) => None + } + } + + pub fn get_origin(&self) -> Bytes { + if self.origin.eq("") { + return Bytes::zero32(); + } + let origin = crate::from_hex(&self.origin).expect("Wrong origin in settings"); + Bytes::from_bytes(origin.as_slice()) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Dns { + pub port: u16, + pub forwarders: Vec +} + +impl Default for Dns { + fn default() -> Self { + Dns { port: 53, forwarders: Vec::new() } + } +} \ No newline at end of file diff --git a/src/simplebus.rs b/src/simplebus.rs index c0ed4dd..a8f281d 100644 --- a/src/simplebus.rs +++ b/src/simplebus.rs @@ -27,6 +27,7 @@ impl Bus { } } +#[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; use std::thread; diff --git a/src/webview/bulma.css b/src/webview/bulma.css index 994a87b..c262663 100644 --- a/src/webview/bulma.css +++ b/src/webview/bulma.css @@ -10838,4 +10838,12 @@ label.panel-block:hover { html { overflow: hidden; +} + +.notification { + position: absolute; + z-index: 100; + width: 50%; + top: 10pt; + right: 10pt; } \ No newline at end of file diff --git a/src/webview/index.html b/src/webview/index.html index ad2a62c..f7b00d9 100644 --- a/src/webview/index.html +++ b/src/webview/index.html @@ -28,8 +28,66 @@ + + + +
-
+
-
- -
- -
-
-
@@ -108,7 +159,14 @@
+
+ +
+
+
+ +
diff --git a/src/webview/scripts.js b/src/webview/scripts.js index 35b551a..c3695f2 100644 --- a/src/webview/scripts.js +++ b/src/webview/scripts.js @@ -1,3 +1,66 @@ +var recordsBuffer = []; + +function addRecord(record) { + recordsBuffer.push(record); + refresh_records_list(); +} + +function delRecord(index) { + recordsBuffer.splice(index, 1); + refresh_records_list(); +} + +function refresh_records_list() { + var buf = ""; + if (recordsBuffer.length > 0) { + buf = "\n"; + } + function getInput(text) { + return ''; + } + + function makeRecord(value, index, array) { + buf += "
\n"; + buf += "
" + getInput(value.domain) + "
\n"; + buf += "
" + getInput(value.type) + "
\n"; + buf += "
" + getInput(value.ttl) + "
\n"; + buf += "
" + getInput(value.addr) + "
\n"; + buf += "
\n
\n"; + buf += "
"; + } + + recordsBuffer.forEach(makeRecord); + document.getElementById("domain_records").innerHTML = buf; +} + +function showNewRecordDialog() { + button_positive = document.getElementById("new_record_positive_button"); + button_positive.onclick = function() { + addRecord(get_record_from_dialog()); // It will refresh list + dialog = document.getElementById("new_record_dialog"); + dialog.className = "modal"; + }; + + button_negative = document.getElementById("new_record_negative_button"); + button_negative.onclick = function() { + dialog = document.getElementById("new_record_dialog"); + dialog.className = "modal"; + refresh_records_list(); + } + + dialog = document.getElementById("new_record_dialog"); + dialog.className = "modal is-active"; +} + +function get_record_from_dialog() { + record_name = document.getElementById("record_name").value; + record_type = document.getElementById("record_type").value; + record_ttl = parseInt(document.getElementById("record_ttl").value); + record_data = document.getElementById("record_data").value; + return { type: record_type, domain: record_name, ttl: record_ttl, addr: record_data } +} + function onLoad() { external.invoke(JSON.stringify({cmd: 'loaded'})); } @@ -37,7 +100,8 @@ function saveKey() { function createDomain() { new_domain = document.getElementById("new_domain").value; - new_dom_records = document.getElementById("new_domain_records").value; + //new_dom_records = document.getElementById("new_domain_records").value; + new_dom_records = JSON.stringify(recordsBuffer); new_dom_tags = document.getElementById("new_domain_tags").value; external.invoke(JSON.stringify({cmd: 'createDomain', name: new_domain, records: new_dom_records, tags: new_dom_tags})); } @@ -102,6 +166,20 @@ function showModalDialog(text, callback) { dialog.className = "modal is-active"; } +function showWarning(text) { + warning = document.getElementById("notification_warning"); + message = document.getElementById("warning_text"); + message.innerHTML = text; + + warning.className = "notification is-warning"; + button = document.getElementById("close"); + button.onclick = function() { + message.value = ""; + warning.className = "notification is-warning is-hidden"; + } + setTimeout(button.onclick, 5000); +} + function showMiningIndicator(visible) { indicator = document.getElementById("mining_indicator"); if (visible) {