Implemented DNS on blockchain. Beautified a lot of code, fixed some things.
This commit is contained in:
@@ -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<String> {
|
||||
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::<Vec<u8>>(1).unwrap().as_slice());
|
||||
let confirmation = Bytes::from_bytes(statement.read::<Vec<u8>>(2).unwrap().as_slice());
|
||||
let method = statement.read::<String>(3).unwrap();
|
||||
let data = statement.read::<String>(4).unwrap();
|
||||
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(5).unwrap().as_slice());
|
||||
let signature = Bytes::from_bytes(statement.read::<Vec<u8>>(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<Block> {
|
||||
self.last_block.clone()
|
||||
}
|
||||
|
||||
@@ -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<Mutex<Context>>
|
||||
}
|
||||
|
||||
impl BlockchainFilter {
|
||||
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
||||
BlockchainFilter { context }
|
||||
}
|
||||
}
|
||||
|
||||
impl DnsFilter for BlockchainFilter {
|
||||
fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket> {
|
||||
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<DnsRecord> = match serde_json::from_str(&data) {
|
||||
Err(_) => { return None; }
|
||||
Ok(records) => { records }
|
||||
};
|
||||
let mut answers: Vec<DnsRecord> = 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
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod transaction;
|
||||
pub mod block;
|
||||
pub mod blockchain;
|
||||
pub mod filter;
|
||||
|
||||
pub use transaction::Transaction;
|
||||
pub use block::Block;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-45
@@ -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<String>
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn new<S: Into<String>>(settings: S) -> serde_json::Result<Settings> {
|
||||
serde_json::from_str(&settings.into())
|
||||
}
|
||||
|
||||
pub fn load(file_name: &str) -> Option<Settings> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
+11
-49
@@ -39,25 +39,15 @@ impl PartialEq<RecordEntry> for RecordEntry {
|
||||
}
|
||||
|
||||
impl Hash for RecordEntry {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
fn hash<H>(&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<Local>,
|
||||
},
|
||||
Records {
|
||||
qtype: QueryType,
|
||||
records: HashSet<RecordEntry>,
|
||||
},
|
||||
NoRecords { qtype: QueryType, ttl: u32, timestamp: DateTime<Local> },
|
||||
Records { qtype: QueryType, records: HashSet<RecordEntry> },
|
||||
}
|
||||
|
||||
#[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<DnsRecord>,
|
||||
increment_stats: bool,
|
||||
) {
|
||||
fn fill_queryresult(&mut self,qname: &str, qtype: QueryType, result_vec: &mut Vec<DnsRecord>, 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<Vec<Arc<DomainEntry>>> {
|
||||
|
||||
+14
-56
@@ -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<DnsPacket>;
|
||||
fn send_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket>;
|
||||
}
|
||||
|
||||
/// 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<DnsPacket> {
|
||||
pub fn send_tcp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||
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<DnsPacket> {
|
||||
/// parallel.
|
||||
pub fn send_udp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||
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<DnsPacket> {
|
||||
fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||
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<StubCallback>) -> 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<DnsPacket> {
|
||||
fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||
(self.callback)(qname, qtype, server, recursive)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -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<Box<dyn DnsFilter + Sync + Send>>,
|
||||
pub client: Box<dyn DnsClient + Sync + Send>,
|
||||
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),
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::dns::protocol::{QueryType, DnsPacket};
|
||||
|
||||
pub trait DnsFilter {
|
||||
fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket>;
|
||||
}
|
||||
|
||||
pub struct DummyFilter {
|
||||
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
impl DnsFilter for DummyFilter {
|
||||
fn lookup(&self, qname: &str, qtype: QueryType) -> Option<DnsPacket> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -22,5 +22,6 @@ pub mod context;
|
||||
pub mod protocol;
|
||||
pub mod resolve;
|
||||
pub mod server;
|
||||
pub mod filter;
|
||||
|
||||
mod netutil;
|
||||
|
||||
+25
-28
@@ -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 {
|
||||
|
||||
+14
-9
@@ -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<ServerContext>, 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<DnsPacket> {
|
||||
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<ServerContext>) -> RecursiveDnsResolver {
|
||||
RecursiveDnsResolver { context: context }
|
||||
RecursiveDnsResolver { context }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-67
@@ -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<DnsPacket>,
|
||||
@@ -112,11 +111,7 @@ pub fn execute_query(context: Arc<ServerContext>, 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<ServerContext>, 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<ServerContext>, 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<ServerContext>, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
|
||||
+111
-20
@@ -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<String> = 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<Mutex<Context>> = 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<Mutex<Context>>, 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<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
||||
@@ -85,7 +142,6 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
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<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
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<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
||||
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::<Vec<DnsRecord>>(&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<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, ke
|
||||
println!("Generating domain {}", name);
|
||||
//let rec_vector: Vec<String> = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect();
|
||||
//let tags_vector: Vec<String> = 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<AtomicBool>) -> Option<Keystore>
|
||||
}
|
||||
}
|
||||
|
||||
fn create_server_context(context: Arc<Mutex<Context>>, settings: &Settings) -> Arc<ServerContext> {
|
||||
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#"<script type="text/javascript">{}</script>"#, 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());
|
||||
}
|
||||
}
|
||||
@@ -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<Mutex<Context>>,
|
||||
|
||||
+2
-3
@@ -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<Duration> = 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>,
|
||||
#[serde(default)]
|
||||
pub dns: Dns
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn new<S: Into<String>>(settings: S) -> serde_json::Result<Settings> {
|
||||
serde_json::from_str(&settings.into())
|
||||
}
|
||||
|
||||
pub fn load(file_name: &str) -> Option<Settings> {
|
||||
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<String>
|
||||
}
|
||||
|
||||
impl Default for Dns {
|
||||
fn default() -> Self {
|
||||
Dns { port: 53, forwarders: Vec::new() }
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ impl<T: Clone> Bus<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
Vendored
+8
@@ -10838,4 +10838,12 @@ label.panel-block:hover {
|
||||
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: 50%;
|
||||
top: 10pt;
|
||||
right: 10pt;
|
||||
}
|
||||
+66
-8
@@ -28,8 +28,66 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="new_record_dialog" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<div class="box">
|
||||
<div class="columns">
|
||||
<div class="column is-one-third">
|
||||
<div class="field">
|
||||
<label class="label">Name</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="www" id="record_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label">Type</label>
|
||||
<div class="select">
|
||||
<select id="record_type">
|
||||
<option>A</option>
|
||||
<option>AAAA</option>
|
||||
<option>CNAME</option>
|
||||
<option>MX</option>
|
||||
<option>TXT</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label">TTL</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="3600" id="record_ttl" value="3600">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="field">
|
||||
<label class="label">Data</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="1.2.3.4" id="record_data">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="buttons is-grouped is-centered">
|
||||
<button class="button is-primary" id="new_record_positive_button">Ok</button>
|
||||
<button class="button is-link is-light" id="new_record_negative_button">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-warning is-hidden" id="notification_warning">
|
||||
<button class="delete" id="close"></button>
|
||||
<p id="warning_text"></p>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-one-fifth">
|
||||
<div class="menu">
|
||||
<ul class="menu-list">
|
||||
@@ -94,13 +152,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Domain records</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" placeholder="@ IN AAAA 200:1111:2222:3333:4444:5555:6666:7777" id="new_domain_records"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Domain tags (will be used for search)</label>
|
||||
<div class="control">
|
||||
@@ -108,7 +159,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content" id="domain_records">
|
||||
<!-- Here will be our domain records, added by dialog -->
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-success" id="add_record_button" onclick="showNewRecordDialog();">Add record</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link" id="new_domain_button" onclick="createDomain();" disabled>Create domain</button>
|
||||
</div>
|
||||
|
||||
+79
-1
@@ -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 = "<label class=\"label\">Records:</label>\n";
|
||||
}
|
||||
function getInput(text) {
|
||||
return '<input class="input" type="text" value="' + text + '" readonly>';
|
||||
}
|
||||
|
||||
function makeRecord(value, index, array) {
|
||||
buf += "<div class=\"columns is-1\">\n";
|
||||
buf += "<div class=\"column\">" + getInput(value.domain) + "</div>\n";
|
||||
buf += "<div class=\"column is-2\">" + getInput(value.type) + "</div>\n";
|
||||
buf += "<div class=\"column is-2\">" + getInput(value.ttl) + "</div>\n";
|
||||
buf += "<div class=\"column\">" + getInput(value.addr) + "</div>\n";
|
||||
buf += "<div class=\"column is-1 align-right\">\n<button class=\"button is-danger is-outlined\" id=\"record_delete\" onclick=\"delRecord(" + index + ");\">";
|
||||
buf += "<span class=\"icon is-small\"><i class=\"fas fa-times\"></i></span></button></div>\n";
|
||||
buf += "</div>";
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user