diff --git a/.gitignore b/.gitignore index 53eaa21..4157dcc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target **/*.rs.bk +/iana-tlds.txt +/iana-hashes.txt diff --git a/Cargo.toml b/Cargo.toml index 427dade..2b07647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,8 @@ winapi = { version = "0.3.7", features = ["impl-default", "wincon"]} thread-priority = "0.2.1" [build-dependencies] +minreq = { version = "2.3.1", features = ["punycode", "https-rustls"] } +rust-crypto = "^0.2" winres = "0.1" [dev-dependencies] diff --git a/build.rs b/build.rs index a2b3ed7..2c63cc7 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,67 @@ extern crate winres; +use std::fs::File; +use std::path::Path; +use std::io::Write; + +use crypto::digest::Digest; +use crypto::sha2::Sha256; + fn main() { if cfg!(target_os = "windows") { let mut res = winres::WindowsResource::new(); res.set_icon("globe_icon.ico"); res.compile().unwrap(); } + + download_iana_zones("iana-tlds.txt", "iana-hashes.txt"); +} + +fn download_iana_zones(zones_name: &str, hashes_name: &str) { + let response = minreq::get("https://data.iana.org/TLD/tlds-alpha-by-domain.txt").send().expect("Could not make request!"); + + let response = response.as_str().expect("Response is not a valid UTF-8!").to_lowercase(); + let list: Vec<_> = response.split("\n").collect(); + let mut zones = String::new(); + let mut hashes = String::new(); + for string in list { + if !string.starts_with("#") && !string.is_empty() { + zones.push_str(string); + zones.push('\n'); + + hashes.push_str(&hash_identity(string)); + hashes.push('\n'); + } + } + + match File::create(Path::new(zones_name)) { + Ok(mut file) => { + file.write_all(zones.trim().as_bytes()).expect("Error saving TLDs file!"); + } + Err(e) => { println!("Error opening TLDs file!\n{}", e); } + } + + match File::create(Path::new(hashes_name)) { + Ok(mut file) => { + file.write_all(hashes.trim().as_bytes()).expect("Error saving TLD-hashes file!"); + } + Err(e) => { println!("Error opening TLD-hashes file!\n{}", e); } + } +} + +fn hash_identity(identity: &str) -> String { + let mut buf: [u8; 32] = [0; 32]; + let mut digest = Sha256::new(); + digest.input_str(identity); + digest.result(&mut buf); + to_hex(&buf) +} + +/// Convert bytes array to HEX format +pub fn to_hex(buf: &[u8]) -> String { + let mut result = String::new(); + for x in buf.iter() { + result.push_str(&format!("{:01$X}", x, 2)); + } + result } \ No newline at end of file diff --git a/src/context.rs b/src/context.rs index 33743ca..0aa8bff 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use crate::{Chain, Bus, Keystore, Settings}; +use crate::{Chain, Bus, Keystore, Settings, Iana}; use crate::event::Event; #[allow(unused_imports)] use log::{trace, debug, info, warn, error}; @@ -7,13 +7,14 @@ pub struct Context { pub settings: Settings, pub keystore: Keystore, pub chain: Chain, + pub iana: Iana, pub bus: Bus, } impl Context { /// Creating an essential context to work with pub fn new(settings: Settings, keystore: Keystore, chain: Chain) -> Context { - Context { settings, keystore, chain, bus: Bus::new() } + Context { settings, keystore, chain, iana: Iana::new(), bus: Bus::new() } } /// Load keystore and return Context diff --git a/src/iana.rs b/src/iana.rs new file mode 100644 index 0000000..da58145 --- /dev/null +++ b/src/iana.rs @@ -0,0 +1,28 @@ +use std::collections::HashSet; + +pub struct Iana { + zones: HashSet, + hashes: HashSet +} + +impl Iana { + pub fn new() -> Self { + let zones: HashSet<_> = include_str!("../iana-tlds.txt") + .split("\n") + .map(String::from) + .collect(); + let hashes: HashSet<_> = include_str!("../iana-hashes.txt") + .split("\n") + .map(String::from) + .collect(); + Self { zones, hashes } + } + + pub fn has_zone(&self, zone: &str) -> bool { + self.zones.contains(zone) + } + + pub fn has_hash(&self, hash: &str) -> bool { + self.hashes.contains(hash) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9032aa3..19b14bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub use crate::p2p::Network; pub use crate::settings::Settings; pub use crate::bytes::Bytes; pub use crate::keys::Keystore; +pub use crate::iana::Iana; pub use crate::simplebus::*; pub use crate::utils::*; @@ -23,4 +24,5 @@ pub mod dns; pub mod dns_utils; pub mod settings; pub mod bytes; +pub mod iana; diff --git a/src/p2p/network.rs b/src/p2p/network.rs index bd7722e..718a5e3 100644 --- a/src/p2p/network.rs +++ b/src/p2p/network.rs @@ -355,6 +355,12 @@ fn handle_message(context: Arc>, message: Message, peers: &mut Pe }; let peer = peers.get_mut_peer(token).unwrap(); peer.set_received_block(block.index); + if let Some(transaction) = &block.transaction { + if context.lock().unwrap().iana.has_hash(&transaction.identity.to_string()) { + // This peer has mined some of the forbidden zones + return State::Banned; + } + } let context = context.clone(); let peers_count = peers.get_peers_active_count(); thread::spawn(move || { diff --git a/src/web_ui.rs b/src/web_ui.rs index da53c6d..8c1a0e5 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -164,11 +164,11 @@ pub fn run_interface(context: Arc>, miner: Arc>) { }; match transaction { None => { - create_domain(miner.clone(), name, records, &keystore); + create_domain(context.clone(), miner.clone(), name, records, &keystore); } Some(transaction) => { if transaction.pub_key == keystore.get_public() { - create_domain(miner.clone(), name, records, &keystore); + create_domain(context.clone(), miner.clone(), name, records, &keystore); } else { warn!("Tried to mine not owned domain!"); let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!")); @@ -185,7 +185,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { TransferDomain { .. } => {} CheckZone { name } => { let name = name.to_lowercase(); - if !check_domain(&name, false) { + if !check_domain(&name, false) || context.lock().unwrap().iana.has_zone(&name) { web_view.eval("zoneAvailable(false)").expect("Error evaluating!"); } else { let c = context.lock().unwrap(); @@ -202,11 +202,11 @@ pub fn run_interface(context: Arc>, miner: Arc>) { }; match transaction { None => { - create_domain(miner.clone(), name, data, &keystore); + create_domain(context.clone(), miner.clone(), name, data, &keystore); } Some(transaction) => { if transaction.pub_key == keystore.get_public() { - create_domain(miner.clone(), name, data, &keystore); + create_domain(context.clone(), miner.clone(), name, data, &keystore); } else { warn!("Tried to mine not owned domain!"); let _ = web_view.eval(&format!("showWarning('{}');", "You cannot change domain that you don't own!")); @@ -253,14 +253,17 @@ pub fn run_interface(context: Arc>, miner: Arc>) { interface.exit(); } -fn create_domain>(miner: Arc>, name: S, data: S, keystore: &Keystore) { +fn create_domain>(context: Arc>, miner: Arc>, name: S, data: S, keystore: &Keystore) { let name = name.into(); info!("Generating domain or zone {}", name); + if context.lock().unwrap().iana.has_zone(&name) { + error!("Unable to mine IANA zone {}!", &name); + return; + } //let tags_vector: Vec = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect(); let transaction = Transaction::from_str(name.into(), "dns".into(), data.into(), keystore.get_public().clone()); let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default()); - let mut miner_guard = miner.lock().unwrap(); - miner_guard.add_block(block); + miner.lock().unwrap().add_block(block); } #[derive(Deserialize)]