Implemented DNS on blockchain. Beautified a lot of code, fixed some things.
This commit is contained in:
+4
-2
@@ -4,10 +4,12 @@ version = "0.1.0"
|
|||||||
authors = ["Revertron <rev@revertron.com>"]
|
authors = ["Revertron <rev@revertron.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
getopts = "0.2.21"
|
||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
@@ -37,4 +39,4 @@ serde_derive = "1.0.27"
|
|||||||
|
|
||||||
[package.metadata.winres]
|
[package.metadata.winres]
|
||||||
ProductName="ALFIS"
|
ProductName="ALFIS"
|
||||||
FileDescription="Alternative Free Identity System for independent DNS and more."
|
FileDescription="Alternative Free Identity System"
|
||||||
@@ -10,5 +10,12 @@
|
|||||||
"127.0.0.1:10000",
|
"127.0.0.1:10000",
|
||||||
"127.0.0.1:10001",
|
"127.0.0.1:10001",
|
||||||
"127.0.0.1:10002"
|
"127.0.0.1:10002"
|
||||||
]
|
],
|
||||||
|
"dns": {
|
||||||
|
"port": 53,
|
||||||
|
"forwarders": [
|
||||||
|
"1.1.1.1",
|
||||||
|
"8.8.8.8"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use sqlite::{Connection, State, Statement};
|
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";
|
const DB_NAME: &str = "blockchain.db";
|
||||||
|
|
||||||
@@ -175,6 +176,30 @@ impl Blockchain {
|
|||||||
true
|
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> {
|
pub fn last_block(&self) -> Option<Block> {
|
||||||
self.last_block.clone()
|
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 transaction;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
|
pub mod filter;
|
||||||
|
|
||||||
pub use transaction::Transaction;
|
pub use transaction::Transaction;
|
||||||
pub use block::Block;
|
pub use block::Block;
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ impl Transaction {
|
|||||||
digest.result(&mut buf);
|
digest.result(&mut buf);
|
||||||
Bytes::from_bytes(&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 {
|
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 crate::event::Event;
|
||||||
use serde::{Serialize, Deserialize};
|
use crate::settings::Settings;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::sync::MutexGuard;
|
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
@@ -43,44 +40,4 @@ impl Context {
|
|||||||
pub fn get_blockchain(&self) -> &Blockchain {
|
pub fn get_blockchain(&self) -> &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 {
|
impl Hash for RecordEntry {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
||||||
where
|
|
||||||
H: Hasher,
|
|
||||||
{
|
|
||||||
self.record.hash(state);
|
self.record.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum RecordSet {
|
pub enum RecordSet {
|
||||||
NoRecords {
|
NoRecords { qtype: QueryType, ttl: u32, timestamp: DateTime<Local> },
|
||||||
qtype: QueryType,
|
Records { qtype: QueryType, records: HashSet<RecordEntry> },
|
||||||
ttl: u32,
|
|
||||||
timestamp: DateTime<Local>,
|
|
||||||
},
|
|
||||||
Records {
|
|
||||||
qtype: QueryType,
|
|
||||||
records: HashSet<RecordEntry>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -70,22 +60,13 @@ pub struct DomainEntry {
|
|||||||
|
|
||||||
impl DomainEntry {
|
impl DomainEntry {
|
||||||
pub fn new(domain: String) -> DomainEntry {
|
pub fn new(domain: String) -> DomainEntry {
|
||||||
DomainEntry {
|
DomainEntry { domain, record_types: HashMap::new(), hits: 0, updates: 0 }
|
||||||
domain: domain,
|
|
||||||
record_types: HashMap::new(),
|
|
||||||
hits: 0,
|
|
||||||
updates: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_nxdomain(&mut self, qtype: QueryType, ttl: u32) {
|
pub fn store_nxdomain(&mut self, qtype: QueryType, ttl: u32) {
|
||||||
self.updates += 1;
|
self.updates += 1;
|
||||||
|
|
||||||
let new_set = RecordSet::NoRecords {
|
let new_set = RecordSet::NoRecords { qtype, ttl, timestamp: Local::now() };
|
||||||
qtype: qtype,
|
|
||||||
ttl: ttl,
|
|
||||||
timestamp: Local::now(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.record_types.insert(qtype, new_set);
|
self.record_types.insert(qtype, new_set);
|
||||||
}
|
}
|
||||||
@@ -93,15 +74,9 @@ impl DomainEntry {
|
|||||||
pub fn store_record(&mut self, rec: &DnsRecord) {
|
pub fn store_record(&mut self, rec: &DnsRecord) {
|
||||||
self.updates += 1;
|
self.updates += 1;
|
||||||
|
|
||||||
let entry = RecordEntry {
|
let entry = RecordEntry { record: rec.clone(), timestamp: Local::now() };
|
||||||
record: rec.clone(),
|
|
||||||
timestamp: Local::now(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(&mut RecordSet::Records {
|
if let Some(&mut RecordSet::Records { ref mut records, .. }) = self.record_types.get_mut(&rec.get_querytype()) {
|
||||||
ref mut records, ..
|
|
||||||
}) = self.record_types.get_mut(&rec.get_querytype())
|
|
||||||
{
|
|
||||||
if records.contains(&entry) {
|
if records.contains(&entry) {
|
||||||
records.remove(&entry);
|
records.remove(&entry);
|
||||||
}
|
}
|
||||||
@@ -113,10 +88,7 @@ impl DomainEntry {
|
|||||||
let mut records = HashSet::new();
|
let mut records = HashSet::new();
|
||||||
records.insert(entry);
|
records.insert(entry);
|
||||||
|
|
||||||
let new_set = RecordSet::Records {
|
let new_set = RecordSet::Records { qtype: rec.get_querytype(), records };
|
||||||
qtype: rec.get_querytype(),
|
|
||||||
records: records,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.record_types.insert(rec.get_querytype(), new_set);
|
self.record_types.insert(rec.get_querytype(), new_set);
|
||||||
}
|
}
|
||||||
@@ -191,9 +163,7 @@ pub struct Cache {
|
|||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
pub fn new() -> Cache {
|
pub fn new() -> Cache {
|
||||||
Cache {
|
Cache { domain_entries: BTreeMap::new() }
|
||||||
domain_entries: BTreeMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cache_state(&mut self, qname: &str, qtype: QueryType) -> CacheState {
|
fn get_cache_state(&mut self, qname: &str, qtype: QueryType) -> CacheState {
|
||||||
@@ -203,13 +173,7 @@ impl Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_queryresult(
|
fn fill_queryresult(&mut self,qname: &str, qtype: QueryType, result_vec: &mut Vec<DnsRecord>, increment_stats: bool) {
|
||||||
&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 let Some(domain_entry) = self.domain_entries.get_mut(qname).and_then(Arc::get_mut) {
|
||||||
if increment_stats {
|
if increment_stats {
|
||||||
domain_entry.hits += 1
|
domain_entry.hits += 1
|
||||||
@@ -275,9 +239,7 @@ pub struct SynchronizedCache {
|
|||||||
|
|
||||||
impl SynchronizedCache {
|
impl SynchronizedCache {
|
||||||
pub fn new() -> SynchronizedCache {
|
pub fn new() -> SynchronizedCache {
|
||||||
SynchronizedCache {
|
SynchronizedCache { cache: RwLock::new(Cache::new()) }
|
||||||
cache: RwLock::new(Cache::new()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(&self) -> Result<Vec<Arc<DomainEntry>>> {
|
pub fn list(&self) -> Result<Vec<Arc<DomainEntry>>> {
|
||||||
|
|||||||
+14
-56
@@ -32,13 +32,7 @@ pub trait DnsClient {
|
|||||||
fn get_failed_count(&self) -> usize;
|
fn get_failed_count(&self) -> usize;
|
||||||
|
|
||||||
fn run(&self) -> Result<()>;
|
fn run(&self) -> Result<()>;
|
||||||
fn send_query(
|
fn send_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket>;
|
||||||
&self,
|
|
||||||
qname: &str,
|
|
||||||
qtype: QueryType,
|
|
||||||
server: (&str, u16),
|
|
||||||
recursive: bool,
|
|
||||||
) -> Result<DnsPacket>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The UDP client
|
/// The UDP client
|
||||||
@@ -72,6 +66,7 @@ struct PendingQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DnsNetworkClient {}
|
unsafe impl Send for DnsNetworkClient {}
|
||||||
|
|
||||||
unsafe impl Sync for DnsNetworkClient {}
|
unsafe impl Sync for DnsNetworkClient {}
|
||||||
|
|
||||||
impl DnsNetworkClient {
|
impl DnsNetworkClient {
|
||||||
@@ -89,13 +84,7 @@ impl DnsNetworkClient {
|
|||||||
///
|
///
|
||||||
/// This is much simpler than using UDP, since the kernel will take care of
|
/// This is much simpler than using UDP, since the kernel will take care of
|
||||||
/// packet ordering, connection state, timeouts etc.
|
/// packet ordering, connection state, timeouts etc.
|
||||||
pub fn send_tcp_query(
|
pub fn send_tcp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||||
&self,
|
|
||||||
qname: &str,
|
|
||||||
qtype: QueryType,
|
|
||||||
server: (&str, u16),
|
|
||||||
recursive: bool,
|
|
||||||
) -> Result<DnsPacket> {
|
|
||||||
let _ = self.total_sent.fetch_add(1, Ordering::Release);
|
let _ = self.total_sent.fetch_add(1, Ordering::Release);
|
||||||
|
|
||||||
// Prepare request
|
// Prepare request
|
||||||
@@ -135,14 +124,8 @@ impl DnsNetworkClient {
|
|||||||
/// The query is sent from the callee thread, but responses are read on a
|
/// 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
|
/// 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
|
/// method is thread safe, and can be used from any number of threads in
|
||||||
/// parallell.
|
/// parallel.
|
||||||
pub fn send_udp_query(
|
pub fn send_udp_query(&self, qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||||
&self,
|
|
||||||
qname: &str,
|
|
||||||
qtype: QueryType,
|
|
||||||
server: (&str, u16),
|
|
||||||
recursive: bool,
|
|
||||||
) -> Result<DnsPacket> {
|
|
||||||
let _ = self.total_sent.fetch_add(1, Ordering::Release);
|
let _ = self.total_sent.fetch_add(1, Ordering::Release);
|
||||||
|
|
||||||
// Prepare request
|
// Prepare request
|
||||||
@@ -156,30 +139,20 @@ impl DnsNetworkClient {
|
|||||||
packet.header.questions = 1;
|
packet.header.questions = 1;
|
||||||
packet.header.recursion_desired = recursive;
|
packet.header.recursion_desired = recursive;
|
||||||
|
|
||||||
packet
|
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
|
||||||
.questions
|
|
||||||
.push(DnsQuestion::new(qname.to_string(), qtype));
|
|
||||||
|
|
||||||
// Create a return channel, and add a `PendingQuery` to the list of lookups
|
// Create a return channel, and add a `PendingQuery` to the list of lookups
|
||||||
// in progress
|
// in progress
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
{
|
{
|
||||||
let mut pending_queries = self
|
let mut pending_queries = self.pending_queries.lock().map_err(|_| ClientError::PoisonedLock)?;
|
||||||
.pending_queries
|
pending_queries.push(PendingQuery { seq: packet.header.id, timestamp: Local::now(), tx });
|
||||||
.lock()
|
|
||||||
.map_err(|_| ClientError::PoisonedLock)?;
|
|
||||||
pending_queries.push(PendingQuery {
|
|
||||||
seq: packet.header.id,
|
|
||||||
timestamp: Local::now(),
|
|
||||||
tx: tx,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send query
|
// Send query
|
||||||
let mut req_buffer = BytePacketBuffer::new();
|
let mut req_buffer = BytePacketBuffer::new();
|
||||||
packet.write(&mut req_buffer, 512)?;
|
packet.write(&mut req_buffer, 512)?;
|
||||||
self.socket
|
self.socket.send_to(&req_buffer.buf[0..req_buffer.pos], server)?;
|
||||||
.send_to(&req_buffer.buf[0..req_buffer.pos], server)?;
|
|
||||||
|
|
||||||
// Wait for response
|
// Wait for response
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
@@ -231,10 +204,7 @@ impl DnsClient for DnsNetworkClient {
|
|||||||
let packet = match DnsPacket::from_buffer(&mut res_buffer) {
|
let packet = match DnsPacket::from_buffer(&mut res_buffer) {
|
||||||
Ok(packet) => packet,
|
Ok(packet) => packet,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!(
|
println!("DnsNetworkClient failed to parse packet with error: {:?}", err);
|
||||||
"DnsNetworkClient failed to parse packet with error: {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -298,13 +268,7 @@ impl DnsClient for DnsNetworkClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_query(
|
fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||||
&self,
|
|
||||||
qname: &str,
|
|
||||||
qtype: QueryType,
|
|
||||||
server: (&str, u16),
|
|
||||||
recursive: bool,
|
|
||||||
) -> Result<DnsPacket> {
|
|
||||||
let packet = self.send_udp_query(qname, qtype, server, recursive)?;
|
let packet = self.send_udp_query(qname, qtype, server, recursive)?;
|
||||||
if !packet.header.truncated_message {
|
if !packet.header.truncated_message {
|
||||||
return Ok(packet);
|
return Ok(packet);
|
||||||
@@ -317,7 +281,6 @@ impl DnsClient for DnsNetworkClient {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dns::protocol::{DnsPacket, DnsRecord, QueryType};
|
use crate::dns::protocol::{DnsPacket, DnsRecord, QueryType};
|
||||||
|
|
||||||
@@ -329,11 +292,12 @@ pub mod tests {
|
|||||||
|
|
||||||
impl<'a> DnsStubClient {
|
impl<'a> DnsStubClient {
|
||||||
pub fn new(callback: Box<StubCallback>) -> DnsStubClient {
|
pub fn new(callback: Box<StubCallback>) -> DnsStubClient {
|
||||||
DnsStubClient { callback: callback }
|
DnsStubClient { callback }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DnsStubClient {}
|
unsafe impl Send for DnsStubClient {}
|
||||||
|
|
||||||
unsafe impl Sync for DnsStubClient {}
|
unsafe impl Sync for DnsStubClient {}
|
||||||
|
|
||||||
impl DnsClient for DnsStubClient {
|
impl DnsClient for DnsStubClient {
|
||||||
@@ -349,13 +313,7 @@ pub mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_query(
|
fn send_query(&self,qname: &str, qtype: QueryType, server: (&str, u16), recursive: bool) -> Result<DnsPacket> {
|
||||||
&self,
|
|
||||||
qname: &str,
|
|
||||||
qtype: QueryType,
|
|
||||||
server: (&str, u16),
|
|
||||||
recursive: bool,
|
|
||||||
) -> Result<DnsPacket> {
|
|
||||||
(self.callback)(qname, qtype, server, recursive)
|
(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::cache::SynchronizedCache;
|
||||||
use crate::dns::client::{DnsClient, DnsNetworkClient};
|
use crate::dns::client::{DnsClient, DnsNetworkClient};
|
||||||
use crate::dns::resolve::{DnsResolver, ForwardingDnsResolver, RecursiveDnsResolver};
|
use crate::dns::resolve::{DnsResolver, ForwardingDnsResolver, RecursiveDnsResolver};
|
||||||
|
use crate::dns::filter::DnsFilter;
|
||||||
|
|
||||||
#[derive(Debug, Display, From, Error)]
|
#[derive(Debug, Display, From, Error)]
|
||||||
pub enum ContextError {
|
pub enum ContextError {
|
||||||
@@ -43,6 +44,7 @@ pub enum ResolveStrategy {
|
|||||||
pub struct ServerContext {
|
pub struct ServerContext {
|
||||||
pub authority: Authority,
|
pub authority: Authority,
|
||||||
pub cache: SynchronizedCache,
|
pub cache: SynchronizedCache,
|
||||||
|
pub filters: Vec<Box<dyn DnsFilter + Sync + Send>>,
|
||||||
pub client: Box<dyn DnsClient + Sync + Send>,
|
pub client: Box<dyn DnsClient + Sync + Send>,
|
||||||
pub dns_port: u16,
|
pub dns_port: u16,
|
||||||
pub api_port: u16,
|
pub api_port: u16,
|
||||||
@@ -66,6 +68,7 @@ impl ServerContext {
|
|||||||
ServerContext {
|
ServerContext {
|
||||||
authority: Authority::new(),
|
authority: Authority::new(),
|
||||||
cache: SynchronizedCache::new(),
|
cache: SynchronizedCache::new(),
|
||||||
|
filters: Vec::new(),
|
||||||
client: Box::new(DnsNetworkClient::new(34255)),
|
client: Box::new(DnsNetworkClient::new(34255)),
|
||||||
dns_port: 53,
|
dns_port: 53,
|
||||||
api_port: 5380,
|
api_port: 5380,
|
||||||
@@ -73,7 +76,7 @@ impl ServerContext {
|
|||||||
allow_recursive: true,
|
allow_recursive: true,
|
||||||
enable_udp: true,
|
enable_udp: true,
|
||||||
enable_tcp: true,
|
enable_tcp: true,
|
||||||
enable_api: true,
|
enable_api: false,
|
||||||
statistics: ServerStatistics {
|
statistics: ServerStatistics {
|
||||||
tcp_query_count: AtomicUsize::new(0),
|
tcp_query_count: AtomicUsize::new(0),
|
||||||
udp_query_count: AtomicUsize::new(0),
|
udp_query_count: AtomicUsize::new(0),
|
||||||
@@ -122,6 +125,7 @@ pub mod tests {
|
|||||||
Arc::new(ServerContext {
|
Arc::new(ServerContext {
|
||||||
authority: Authority::new(),
|
authority: Authority::new(),
|
||||||
cache: SynchronizedCache::new(),
|
cache: SynchronizedCache::new(),
|
||||||
|
filters: Vec::new(),
|
||||||
client: Box::new(DnsStubClient::new(callback)),
|
client: Box::new(DnsStubClient::new(callback)),
|
||||||
dns_port: 53,
|
dns_port: 53,
|
||||||
api_port: 5380,
|
api_port: 5380,
|
||||||
@@ -129,7 +133,7 @@ pub mod tests {
|
|||||||
allow_recursive: true,
|
allow_recursive: true,
|
||||||
enable_udp: true,
|
enable_udp: true,
|
||||||
enable_tcp: true,
|
enable_tcp: true,
|
||||||
enable_api: true,
|
enable_api: false,
|
||||||
statistics: ServerStatistics {
|
statistics: ServerStatistics {
|
||||||
tcp_query_count: AtomicUsize::new(0),
|
tcp_query_count: AtomicUsize::new(0),
|
||||||
udp_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 protocol;
|
||||||
pub mod resolve;
|
pub mod resolve;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
pub mod filter;
|
||||||
|
|
||||||
mod netutil;
|
mod netutil;
|
||||||
|
|||||||
+25
-28
@@ -188,8 +188,8 @@ impl DnsRecord {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(DnsRecord::A {
|
Ok(DnsRecord::A {
|
||||||
domain: domain,
|
domain,
|
||||||
addr: addr,
|
addr,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -210,8 +210,8 @@ impl DnsRecord {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(DnsRecord::AAAA {
|
Ok(DnsRecord::AAAA {
|
||||||
domain: domain,
|
domain,
|
||||||
addr: addr,
|
addr,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ impl DnsRecord {
|
|||||||
buffer.read_qname(&mut ns)?;
|
buffer.read_qname(&mut ns)?;
|
||||||
|
|
||||||
Ok(DnsRecord::NS {
|
Ok(DnsRecord::NS {
|
||||||
domain: domain,
|
domain,
|
||||||
host: ns,
|
host: ns,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
@@ -230,7 +230,7 @@ impl DnsRecord {
|
|||||||
buffer.read_qname(&mut cname)?;
|
buffer.read_qname(&mut cname)?;
|
||||||
|
|
||||||
Ok(DnsRecord::CNAME {
|
Ok(DnsRecord::CNAME {
|
||||||
domain: domain,
|
domain,
|
||||||
host: cname,
|
host: cname,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
@@ -244,10 +244,10 @@ impl DnsRecord {
|
|||||||
buffer.read_qname(&mut srv)?;
|
buffer.read_qname(&mut srv)?;
|
||||||
|
|
||||||
Ok(DnsRecord::SRV {
|
Ok(DnsRecord::SRV {
|
||||||
domain: domain,
|
domain,
|
||||||
priority: priority,
|
priority,
|
||||||
weight: weight,
|
weight,
|
||||||
port: port,
|
port,
|
||||||
host: srv,
|
host: srv,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
@@ -258,8 +258,8 @@ impl DnsRecord {
|
|||||||
buffer.read_qname(&mut mx)?;
|
buffer.read_qname(&mut mx)?;
|
||||||
|
|
||||||
Ok(DnsRecord::MX {
|
Ok(DnsRecord::MX {
|
||||||
domain: domain,
|
domain,
|
||||||
priority: priority,
|
priority,
|
||||||
host: mx,
|
host: mx,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
@@ -278,14 +278,14 @@ impl DnsRecord {
|
|||||||
let minimum = buffer.read_u32()?;
|
let minimum = buffer.read_u32()?;
|
||||||
|
|
||||||
Ok(DnsRecord::SOA {
|
Ok(DnsRecord::SOA {
|
||||||
domain: domain,
|
domain,
|
||||||
m_name: m_name,
|
m_name,
|
||||||
r_name: r_name,
|
r_name,
|
||||||
serial: serial,
|
serial,
|
||||||
refresh: refresh,
|
refresh,
|
||||||
retry: retry,
|
retry,
|
||||||
expire: expire,
|
expire,
|
||||||
minimum: minimum,
|
minimum,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -300,7 +300,7 @@ impl DnsRecord {
|
|||||||
buffer.step(data_len as usize)?;
|
buffer.step(data_len as usize)?;
|
||||||
|
|
||||||
Ok(DnsRecord::TXT {
|
Ok(DnsRecord::TXT {
|
||||||
domain: domain,
|
domain,
|
||||||
data: txt,
|
data: txt,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
@@ -317,16 +317,16 @@ impl DnsRecord {
|
|||||||
Ok(DnsRecord::OPT {
|
Ok(DnsRecord::OPT {
|
||||||
packet_len: class,
|
packet_len: class,
|
||||||
flags: ttl,
|
flags: ttl,
|
||||||
data: data,
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
QueryType::UNKNOWN(_) => {
|
QueryType::UNKNOWN(_) => {
|
||||||
buffer.step(data_len as usize)?;
|
buffer.step(data_len as usize)?;
|
||||||
|
|
||||||
Ok(DnsRecord::UNKNOWN {
|
Ok(DnsRecord::UNKNOWN {
|
||||||
domain: domain,
|
domain,
|
||||||
qtype: qtype_num,
|
qtype: qtype_num,
|
||||||
data_len: data_len,
|
data_len,
|
||||||
ttl: TransientTtl(ttl),
|
ttl: TransientTtl(ttl),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -755,10 +755,7 @@ pub struct DnsQuestion {
|
|||||||
|
|
||||||
impl DnsQuestion {
|
impl DnsQuestion {
|
||||||
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
|
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
|
||||||
DnsQuestion {
|
DnsQuestion { name, qtype }
|
||||||
name: name,
|
|
||||||
qtype: qtype,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binary_len(&self) -> usize {
|
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)
|
self.perform(qname, qtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +73,7 @@ pub struct ForwardingDnsResolver {
|
|||||||
|
|
||||||
impl ForwardingDnsResolver {
|
impl ForwardingDnsResolver {
|
||||||
pub fn new(context: Arc<ServerContext>, server: (String, u16)) -> ForwardingDnsResolver {
|
pub fn new(context: Arc<ServerContext>, server: (String, u16)) -> ForwardingDnsResolver {
|
||||||
ForwardingDnsResolver {
|
ForwardingDnsResolver { context, server }
|
||||||
context: context,
|
|
||||||
server: server,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +84,12 @@ impl DnsResolver for ForwardingDnsResolver {
|
|||||||
|
|
||||||
fn perform(&mut self, qname: &str, qtype: QueryType) -> Result<DnsPacket> {
|
fn perform(&mut self, qname: &str, qtype: QueryType) -> Result<DnsPacket> {
|
||||||
let &(ref host, port) = &self.server;
|
let &(ref host, port) = &self.server;
|
||||||
let result = self
|
let result = match self.context.cache.lookup(qname, qtype) {
|
||||||
.context
|
None => {
|
||||||
.client
|
self.context.client.send_query(qname, qtype, (host.as_str(), port), true)?
|
||||||
.send_query(qname, qtype, (host.as_str(), port), true)?;
|
}
|
||||||
|
Some(packet) => packet
|
||||||
|
};
|
||||||
|
|
||||||
self.context.cache.store(&result.answers)?;
|
self.context.cache.store(&result.answers)?;
|
||||||
|
|
||||||
@@ -101,7 +106,7 @@ pub struct RecursiveDnsResolver {
|
|||||||
|
|
||||||
impl RecursiveDnsResolver {
|
impl RecursiveDnsResolver {
|
||||||
pub fn new(context: Arc<ServerContext>) -> 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
|
/// Utility function for resolving domains referenced in for example CNAME or SRV
|
||||||
/// records. This usually spares the client from having to perform additional
|
/// records. This usually spares the client from having to perform additional lookups.
|
||||||
/// lookups.
|
|
||||||
fn resolve_cnames(
|
fn resolve_cnames(
|
||||||
lookup_list: &[DnsRecord],
|
lookup_list: &[DnsRecord],
|
||||||
results: &mut Vec<DnsPacket>,
|
results: &mut Vec<DnsPacket>,
|
||||||
@@ -112,11 +111,7 @@ pub fn execute_query(context: Arc<ServerContext>, request: &DnsPacket) -> DnsPac
|
|||||||
packet.questions.push(question.clone());
|
packet.questions.push(question.clone());
|
||||||
|
|
||||||
let mut resolver = context.create_resolver(context.clone());
|
let mut resolver = context.create_resolver(context.clone());
|
||||||
let rescode = match resolver.resolve(
|
let rescode = match resolver.resolve(&question.name, question.qtype, request.header.recursion_desired) {
|
||||||
&question.name,
|
|
||||||
question.qtype,
|
|
||||||
request.header.recursion_desired,
|
|
||||||
) {
|
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let rescode = result.header.rescode;
|
let rescode = result.header.rescode;
|
||||||
|
|
||||||
@@ -128,10 +123,7 @@ pub fn execute_query(context: Arc<ServerContext>, request: &DnsPacket) -> DnsPac
|
|||||||
rescode
|
rescode
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!(
|
println!("Failed to resolve {:?} {}: {:?}", question.qtype, question.name, err);
|
||||||
"Failed to resolve {:?} {}: {:?}",
|
|
||||||
question.qtype, question.name, err
|
|
||||||
);
|
|
||||||
ResultCode::SERVFAIL
|
ResultCode::SERVFAIL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -169,10 +161,10 @@ pub struct DnsUdpServer {
|
|||||||
impl DnsUdpServer {
|
impl DnsUdpServer {
|
||||||
pub fn new(context: Arc<ServerContext>, thread_count: usize) -> DnsUdpServer {
|
pub fn new(context: Arc<ServerContext>, thread_count: usize) -> DnsUdpServer {
|
||||||
DnsUdpServer {
|
DnsUdpServer {
|
||||||
context: context,
|
context,
|
||||||
request_queue: Arc::new(Mutex::new(VecDeque::new())),
|
request_queue: Arc::new(Mutex::new(VecDeque::new())),
|
||||||
request_cond: Arc::new(Condvar::new()),
|
request_cond: Arc::new(Condvar::new()),
|
||||||
thread_count: thread_count,
|
thread_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,11 +172,10 @@ impl DnsUdpServer {
|
|||||||
impl DnsServer for DnsUdpServer {
|
impl DnsServer for DnsUdpServer {
|
||||||
/// Launch the server
|
/// Launch the server
|
||||||
///
|
///
|
||||||
/// This method takes ownership of the server, preventing the method from
|
/// This method takes ownership of the server, preventing the method from being called multiple times.
|
||||||
/// being called multiple times.
|
|
||||||
fn run_server(self) -> Result<()> {
|
fn run_server(self) -> Result<()> {
|
||||||
// Bind the socket
|
// 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
|
// Spawn threads for handling requests
|
||||||
for thread_id in 0..self.thread_count {
|
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
|
// Create a response buffer, and ask the context for an appropriate resolver
|
||||||
// resolver
|
|
||||||
let mut res_buffer = VectorPacketBuffer::new();
|
let mut res_buffer = VectorPacketBuffer::new();
|
||||||
|
|
||||||
let mut packet = execute_query(context.clone(), &request);
|
let mut packet = execute_query(context.clone(), &request);
|
||||||
@@ -236,14 +226,8 @@ impl DnsServer for DnsUdpServer {
|
|||||||
|
|
||||||
// Fire off the response
|
// Fire off the response
|
||||||
let len = res_buffer.pos();
|
let len = res_buffer.pos();
|
||||||
let data = return_or_report!(
|
let data = return_or_report!(res_buffer.get_range(0, len), "Failed to get buffer data");
|
||||||
res_buffer.get_range(0, len),
|
ignore_or_report!(socket_clone.send_to(data, src), "Failed to send response packet");
|
||||||
"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())
|
.name("DnsUdpServer-incoming".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
let _ = self
|
let _ = self.context.statistics.udp_query_count.fetch_add(1, Ordering::Release);
|
||||||
.context
|
|
||||||
.statistics
|
|
||||||
.udp_query_count
|
|
||||||
.fetch_add(1, Ordering::Release);
|
|
||||||
|
|
||||||
// Read a query packet
|
// Read a query packet
|
||||||
let mut req_buffer = BytePacketBuffer::new();
|
let mut req_buffer = BytePacketBuffer::new();
|
||||||
@@ -278,8 +258,7 @@ impl DnsServer for DnsUdpServer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Acquire lock, add request to queue, and notify waiting threads
|
// Acquire lock, add request to queue, and notify waiting threads using the condition.
|
||||||
// using the condition.
|
|
||||||
match self.request_queue.lock() {
|
match self.request_queue.lock() {
|
||||||
Ok(mut queue) => {
|
Ok(mut queue) => {
|
||||||
queue.push_back((src, request));
|
queue.push_back((src, request));
|
||||||
@@ -305,17 +284,13 @@ pub struct DnsTcpServer {
|
|||||||
|
|
||||||
impl DnsTcpServer {
|
impl DnsTcpServer {
|
||||||
pub fn new(context: Arc<ServerContext>, thread_count: usize) -> DnsTcpServer {
|
pub fn new(context: Arc<ServerContext>, thread_count: usize) -> DnsTcpServer {
|
||||||
DnsTcpServer {
|
DnsTcpServer { context, senders: Vec::new(), thread_count }
|
||||||
context: context,
|
|
||||||
senders: Vec::new(),
|
|
||||||
thread_count: thread_count,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnsServer for DnsTcpServer {
|
impl DnsServer for DnsTcpServer {
|
||||||
fn run_server(mut self) -> Result<()> {
|
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
|
// Spawn threads for handling requests, and create the channels
|
||||||
for thread_id in 0..self.thread_count {
|
for thread_id in 0..self.thread_count {
|
||||||
@@ -332,48 +307,30 @@ impl DnsServer for DnsTcpServer {
|
|||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = context
|
let _ = context.statistics.tcp_query_count.fetch_add(1, Ordering::Release);
|
||||||
.statistics
|
|
||||||
.tcp_query_count
|
|
||||||
.fetch_add(1, Ordering::Release);
|
|
||||||
|
|
||||||
// When DNS packets are sent over TCP, they're prefixed with a two byte
|
// 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
|
// length. We don't really need to know the length in advance, so we
|
||||||
// just move past it and continue reading as usual
|
// just move past it and continue reading as usual
|
||||||
ignore_or_report!(
|
ignore_or_report!(read_packet_length(&mut stream), "Failed to read query packet length");
|
||||||
read_packet_length(&mut stream),
|
|
||||||
"Failed to read query packet length"
|
|
||||||
);
|
|
||||||
|
|
||||||
let request = {
|
let request = {
|
||||||
let mut stream_buffer = StreamPacketBuffer::new(&mut stream);
|
let mut stream_buffer = StreamPacketBuffer::new(&mut stream);
|
||||||
return_or_report!(
|
return_or_report!(DnsPacket::from_buffer(&mut stream_buffer), "Failed to read query packet")
|
||||||
DnsPacket::from_buffer(&mut stream_buffer),
|
|
||||||
"Failed to read query packet"
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res_buffer = VectorPacketBuffer::new();
|
let mut res_buffer = VectorPacketBuffer::new();
|
||||||
|
|
||||||
let mut packet = execute_query(context.clone(), &request);
|
let mut packet = execute_query(context.clone(), &request);
|
||||||
ignore_or_report!(
|
ignore_or_report!(packet.write(&mut res_buffer, 0xFFFF), "Failed to write packet to buffer");
|
||||||
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
|
// As is the case for incoming queries, we need to send a 2 byte length
|
||||||
// value before handing of the actual packet.
|
// value before handing of the actual packet.
|
||||||
let len = res_buffer.pos();
|
let len = res_buffer.pos();
|
||||||
ignore_or_report!(
|
ignore_or_report!(write_packet_length(&mut stream, len), "Failed to write packet size");
|
||||||
write_packet_length(&mut stream, len),
|
|
||||||
"Failed to write packet size"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now we can go ahead and write the actual packet
|
// Now we can go ahead and write the actual packet
|
||||||
let data = return_or_report!(
|
let data = return_or_report!(res_buffer.get_range(0, len), "Failed to get packet data");
|
||||||
res_buffer.get_range(0, len),
|
|
||||||
"Failed to get packet data"
|
|
||||||
);
|
|
||||||
|
|
||||||
ignore_or_report!(stream.write(data), "Failed to write response packet");
|
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) {
|
match self.senders[thread_no].send(stream) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(
|
println!("Failed to send TCP request for processing on thread {}: {}", thread_no, e);
|
||||||
"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::blockchain::Blockchain;
|
||||||
pub use crate::context::Context;
|
pub use crate::context::Context;
|
||||||
pub use crate::context::Settings;
|
pub use settings::Settings;
|
||||||
pub use crate::keys::Bytes;
|
pub use crate::keys::Bytes;
|
||||||
pub use crate::keys::Keystore;
|
pub use crate::keys::Keystore;
|
||||||
pub use crate::simplebus::*;
|
pub use crate::simplebus::*;
|
||||||
pub use crate::utils::*;
|
pub use crate::utils::*;
|
||||||
|
|
||||||
mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod simplebus;
|
pub mod simplebus;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
@@ -18,4 +18,5 @@ pub mod context;
|
|||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod p2p;
|
pub mod p2p;
|
||||||
pub mod dns;
|
pub mod dns;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
|
|||||||
+111
-20
@@ -2,22 +2,31 @@
|
|||||||
extern crate web_view;
|
extern crate web_view;
|
||||||
extern crate tinyfiledialogs as tfd;
|
extern crate tinyfiledialogs as tfd;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde::{Deserialize};
|
use serde::Deserialize;
|
||||||
use web_view::*;
|
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::event::Event;
|
||||||
use alfis::miner::Miner;
|
use alfis::miner::Miner;
|
||||||
use alfis::p2p::Network;
|
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;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
const ONE_YEAR: u16 = 365;
|
const ONE_YEAR: u16 = 365;
|
||||||
const GENESIS_ZONE: &str = "ygg";
|
const GENESIS_ZONE: &str = "ygg";
|
||||||
const GENESIS_ZONE_DIFFICULTY: u16 = 20;
|
const GENESIS_ZONE_DIFFICULTY: u16 = 20;
|
||||||
@@ -26,6 +35,27 @@ const SETTINGS_FILENAME: &str = "alfis.cfg";
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("ALFIS 0.1.0");
|
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 settings = Settings::load(SETTINGS_FILENAME).expect("Error loading settings");
|
||||||
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
let keystore: Keystore = match Keystore::from_file(&settings.key_file, "") {
|
||||||
None => {
|
None => {
|
||||||
@@ -39,7 +69,9 @@ fn main() {
|
|||||||
None => { println!("No blocks found in DB"); }
|
None => { println!("No blocks found in DB"); }
|
||||||
Some(block) => { println!("Loaded DB with origin {:?}", &block.hash); }
|
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)));
|
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());
|
let mut miner_obj = Miner::new(context.clone());
|
||||||
miner_obj.start_mining_thread();
|
miner_obj.start_mining_thread();
|
||||||
@@ -49,7 +81,32 @@ fn main() {
|
|||||||
network.start().expect("Error starting network component");
|
network.start().expect("Error starting network component");
|
||||||
|
|
||||||
create_genesis_if_needed(&context, &miner);
|
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>>) {
|
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 => {
|
Loaded => {
|
||||||
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
|
web_view.eval("showMiningIndicator(false);").expect("Error evaluating!");
|
||||||
let handle = web_view.handle();
|
let handle = web_view.handle();
|
||||||
let context_copy = context.clone();
|
|
||||||
let mut c = context.lock().unwrap();
|
let mut c = context.lock().unwrap();
|
||||||
c.bus.register(move |_uuid, e| {
|
c.bus.register(move |_uuid, e| {
|
||||||
println!("Got event from bus {:?}", &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() {
|
if !eval.is_empty() {
|
||||||
println!("Evaluating {}", &eval);
|
println!("Evaluating {}", &eval);
|
||||||
handle.dispatch(move |web_view| {
|
handle.dispatch(move |web_view| {
|
||||||
web_view.eval(&eval.replace("\\", "\\\\")).expect("Error evaluating!");
|
web_view.eval(&eval.replace("\\", "\\\\"))
|
||||||
return WVResult::Ok(());
|
|
||||||
}).expect("Error dispatching!");
|
}).expect("Error dispatching!");
|
||||||
}
|
}
|
||||||
true
|
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());
|
let available = c.get_blockchain().is_domain_available(&name, &c.get_keystore());
|
||||||
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
web_view.eval(&format!("domainAvailable({})", available)).expect("Error evaluating!");
|
||||||
}
|
}
|
||||||
CreateDomain { name, records, tags } => {
|
CreateDomain { name, records, .. } => {
|
||||||
let keystore = {
|
println!("Got records: {}", records);
|
||||||
let guard = context.lock().unwrap();
|
if serde_json::from_str::<Vec<DnsRecord>>(&records).is_ok() {
|
||||||
guard.get_keystore()
|
let keystore = {
|
||||||
};
|
let guard = context.lock().unwrap();
|
||||||
create_domain(miner.clone(), name, records, &keystore);
|
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 } => {
|
ChangeDomain { .. } => {}
|
||||||
let keystore = { context.lock().unwrap().get_keystore() };
|
RenewDomain { .. } => {}
|
||||||
// TODO
|
TransferDomain { .. } => {}
|
||||||
}
|
|
||||||
RenewDomain { name, days } => {}
|
|
||||||
TransferDomain { name, owner } => {}
|
|
||||||
StopMining => {
|
StopMining => {
|
||||||
context.lock().unwrap().bus.post(Event::ActionStopMining);
|
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);
|
println!("Generating domain {}", name);
|
||||||
//let rec_vector: Vec<String> = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect();
|
//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 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();
|
let mut miner_guard = miner.lock().unwrap();
|
||||||
miner_guard.add_transaction(transaction);
|
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)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
@@ -281,3 +356,19 @@ fn inline_style(s: &str) -> String {
|
|||||||
fn inline_script(s: &str) -> String {
|
fn inline_script(s: &str) -> String {
|
||||||
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
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::{Block, Bytes, Context, hash_is_good, Transaction};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use std::ops::DerefMut;
|
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
context: Arc<Mutex<Context>>,
|
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 crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
||||||
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
|
use std::net::{SocketAddr, IpAddr, SocketAddrV4, Shutdown};
|
||||||
use std::ops::DerefMut;
|
|
||||||
|
|
||||||
const SERVER: Token = Token(0);
|
const SERVER: Token = Token(0);
|
||||||
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
||||||
@@ -95,8 +94,8 @@ impl Network {
|
|||||||
None => {}
|
None => {}
|
||||||
Some(mut peer) => {
|
Some(mut peer) => {
|
||||||
let stream = peer.get_stream();
|
let stream = peer.get_stream();
|
||||||
poll.registry().deregister(stream);
|
let _ = poll.registry().deregister(stream);
|
||||||
stream.shutdown(Shutdown::Both);
|
let _ = stream.shutdown(Shutdown::Both);
|
||||||
println!("Peer connection {:?} has shut down", &peer.get_addr());
|
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 {
|
mod tests {
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|||||||
Vendored
+8
@@ -10838,4 +10838,12 @@ label.panel-block:hover {
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
width: 50%;
|
||||||
|
top: 10pt;
|
||||||
|
right: 10pt;
|
||||||
}
|
}
|
||||||
+66
-8
@@ -28,8 +28,66 @@
|
|||||||
</div>
|
</div>
|
||||||
</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="container">
|
||||||
<div class="columns">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-one-fifth">
|
<div class="column is-one-fifth">
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
@@ -94,13 +152,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="field">
|
||||||
<label class="label">Domain tags (will be used for search)</label>
|
<label class="label">Domain tags (will be used for search)</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
@@ -108,7 +159,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</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="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-success" id="add_record_button" onclick="showNewRecordDialog();">Add record</button>
|
||||||
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-link" id="new_domain_button" onclick="createDomain();" disabled>Create domain</button>
|
<button class="button is-link" id="new_domain_button" onclick="createDomain();" disabled>Create domain</button>
|
||||||
</div>
|
</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() {
|
function onLoad() {
|
||||||
external.invoke(JSON.stringify({cmd: 'loaded'}));
|
external.invoke(JSON.stringify({cmd: 'loaded'}));
|
||||||
}
|
}
|
||||||
@@ -37,7 +100,8 @@ function saveKey() {
|
|||||||
|
|
||||||
function createDomain() {
|
function createDomain() {
|
||||||
new_domain = document.getElementById("new_domain").value;
|
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;
|
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}));
|
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";
|
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) {
|
function showMiningIndicator(visible) {
|
||||||
indicator = document.getElementById("mining_indicator");
|
indicator = document.getElementById("mining_indicator");
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|||||||
Reference in New Issue
Block a user