Implemented DNS on blockchain. Beautified a lot of code, fixed some things.

pull/2/head
Revertron 3 years ago
parent 4b5e5112da
commit d135204af7

@ -4,10 +4,12 @@ version = "0.1.0"
authors = ["Revertron <rev@revertron.com>"]
edition = "2018"
build = "build.rs"
#![windows_subsystem = "windows"]
homepage = "https://alfis.name"
repository = "https://github.com/Revertron/Alfis"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
getopts = "0.2.21"
rust-crypto = "^0.2"
num_cpus = "1.13.0"
byteorder = "1.3.2"
@ -37,4 +39,4 @@ serde_derive = "1.0.27"
[package.metadata.winres]
ProductName="ALFIS"
FileDescription="Alternative Free Identity System for independent DNS and more."
FileDescription="Alternative Free Identity System"

@ -10,5 +10,12 @@
"127.0.0.1:10000",
"127.0.0.1:10001",
"127.0.0.1:10002"
]
],
"dns": {
"port": 53,
"forwarders": [
"1.1.1.1",
"8.8.8.8"
]
}
}

@ -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 {

@ -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())
}
}

@ -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>>> {

@ -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)
}
}

@ -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;

@ -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 {

@ -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 }
}
}

@ -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,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;

@ -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);
}
ChangeDomain { name, records, tags } => {
let keystore = { context.lock().unwrap().get_keystore() };
// TODO
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."));
}
}
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>>,

@ -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;

@ -10838,4 +10838,12 @@ label.panel-block:hover {
html {
overflow: hidden;
}
.notification {
position: absolute;
z-index: 100;
width: 50%;
top: 10pt;
right: 10pt;
}

@ -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>

@ -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) {

Loading…
Cancel
Save