diff --git a/Cargo.toml b/Cargo.toml index 155e078..c132bf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,5 @@ name = "client" path = "src/client.rs" [[bin]] -name = "dns" +name = "main" path = "src/main.rs" \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index 1ef25be..7dc8798 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,3 @@ -#![feature(proc_macro_hygiene, decl_macro)] - -#[macro_use] -extern crate rocket; - use futures::try_join; use std::io; use tokio::task; @@ -21,33 +16,23 @@ use trust_dns_server::authority::{ }; -fn simple_test() { -// let address = "127.0.0.1:12323".parse().unwrap(); +pub fn check_domain_available(domain: &str) -> bool { let address = "167.99.136.83:53".parse().unwrap(); let conn = UdpClientConnection::new(address).unwrap(); let client = SyncClient::new(conn); -// let conn = TcpClientConnection::new(address).unwrap(); -// let client = SyncClient::new(conn); + let name = Name::from_str(domain).unwrap(); - // Specify the name, note the final '.' which specifies it's an FQDN - let name = Name::from_str("time.commoninternet.net.").unwrap(); - - // NOTE: see 'Setup a connection' example above - // Send the query and get a message response, see RecordType for all supported options - println!("++ making query"); + info!("++ making query {:?}", domain); let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap(); - println!("++ received response"); - - // Messages are the packets sent between client and server in DNS, DnsResonse's can be - // dereferenced to a Message. There are many fields to a Message, It's beyond the scope - // of these examples to explain them. See trust_dns::op::message::Message for more details. - // generally we will be interested in the Message::answers + info!("++ received response"); let answers: &[Record] = response.answers(); - // Records are generic objects which can contain any data. - // In order to access it we need to first check what type of record it is - // In this case we are interested in A, IPv4 address - println!("found: {:?}", answers[0].rdata()) + if answers.len() > 0 { + info!("found: {:?}", answers[0].rdata()); + true + } else { + false + } } fn update_test() { @@ -70,6 +55,6 @@ fn update_test() { fn main() { -// simple_test(); - update_test(); + check_domain_available("test"); +// update_test(); } diff --git a/src/dns.rs b/src/dns.rs deleted file mode 100644 index 91b0e29..0000000 --- a/src/dns.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::collections::BTreeMap; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::str::FromStr; -use std::sync::{Arc, RwLock}; -use std::time::Duration; -use tokio::net::TcpListener; -use tokio::net::UdpSocket; -use trust_dns_client::rr::rdata::soa::SOA; -use trust_dns_client::rr::{LowerName, Name, RData, Record, RecordSet, RecordType, RrKey}; -use trust_dns_server::authority::{Catalog, ZoneType}; -use trust_dns_server::server::ServerFuture; -use trust_dns_server::store::in_memory::InMemoryAuthority; -use std::env; -use dotenv; - -static DEFAULT_TCP_REQUEST_TIMEOUT: u64 = 5; - - -struct DnsManager { - catalog: Catalog, - dyn_root_zone: String, -} - -impl DnsManager { - pub fn new(dyn_root_zone: String) -> DnsManager { - - let catalog: Catalog = Catalog::new(); - - return DnsManager { - catalog, - dyn_root_zone, - }; - } - - fn get_initial_records(domain: &str) -> BTreeMap { - let authority_name = Name::from_str(domain).unwrap(); - let soa_serial = 1; - let soa_name = Name::from_str(domain).unwrap(); - let soa_rdata = RData::SOA(SOA::new( - Name::from_str(domain).unwrap(), // mname - Name::from_str(&format!("root.{}", domain)).unwrap(), // rname - soa_serial, // serial - 604800, // refresh - 86400, // retry - 2419200, // expire - 86400, // negtive cache ttl - )); - let mut soa_record_set = RecordSet::new(&soa_name, RecordType::SOA, soa_serial); - soa_record_set.add_rdata(soa_rdata); - let soa_rr_key = RrKey::new( - LowerName::new(&authority_name), - soa_record_set.record_type(), - ); - let mut authority_records = BTreeMap::new(); - authority_records.insert(soa_rr_key, soa_record_set); - authority_records - } - - fn upsert_domain(mut authority: InMemoryAuthority, domain: String, ip: Ipv4Addr) -> InMemoryAuthority { - let dyn_name = Name::from_str(&domain).unwrap(); - let dyn_ttl = 60; - let dyn_rdata = RData::A(ip); - let dyn_record = Record::from_rdata(dyn_name, dyn_ttl, dyn_rdata); - authority.upsert(dyn_record, authority.serial()); - authority - } - - fn build_catalog(&mut self) { - let authority_records = DnsManager::get_initial_records(&self.dyn_root_zone); - let authority_name = Name::from_str(&self.dyn_root_zone).unwrap(); - - let authority_zone_type = ZoneType::Master; - let authority_allow_axfr = false; - - // first create an authority for root_dyn_zone - let mut authority = InMemoryAuthority::new( - authority_name.clone(), - authority_records, - authority_zone_type, - authority_allow_axfr, - ) - .unwrap(); - - // then upsert records into the authority for all records in database - let domain1 = format!("test.{}", self.dyn_root_zone); - let ip1 = Ipv4Addr::new(1, 1, 1, 1); - authority = DnsManager::upsert_domain(authority, domain1, ip1); - - let domain2 = format!("peach.{}", self.dyn_root_zone); - let ip2 = Ipv4Addr::new(1, 1, 1, 3); - authority = DnsManager::upsert_domain(authority, domain2, ip2); - - // finally put the authority into the catalog - self.catalog.upsert( - LowerName::new(&authority_name), - Box::new(Arc::new(RwLock::new(authority))), - ); - } - - - fn upsert_test(&mut self) { - - // first insert the authority for the root dyn zone - self.build_catalog(); - - // second upsert, for sub-sub - - // third upsert, for sub-sub -// let domain2 = &format!("peach.{}", self.dyn_root_zone); -// let ip2 = Ipv4Addr::new(1, 1, 1, 2); -// self.upsert(domain2, ip2); -// -// // update upsert, for sub-sub -// let domain2 = &format!("test.{}", self.dyn_root_zone); -// let ip2 = Ipv4Addr::new(1, 1, 1, 3); -// self.upsert(domain2, ip2); - - } -} - - -pub async fn server() -> ServerFuture { - info!("Trust-DNS {} starting", trust_dns_server::version()); - - dotenv::from_path("/etc/peach-dyndns.conf").ok(); - let dyn_root_zone = env::var("DYN_ROOT_ZONE").expect("DYN_ROOT_ZONE not set"); - let mut dns_manager = DnsManager::new(dyn_root_zone.to_string()); - -// // first insert -// dns_manager.upsert( -// "test.dyn.peachcloud.org".to_string(), -// Ipv4Addr::new(1, 1, 1, 1), -// ); -// -// // second insert -// dns_manager.upsert( -// "test.dyn.peachcloud.org".to_string(), -// Ipv4Addr::new(1, 1, 1, 3), -// ); -// -// // third insert -// dns_manager.upsert( -// "peach.dyn.peachcloud.org".to_string(), -// Ipv4Addr::new(1, 1 , 2, 3), -// ); - - dns_manager.upsert_test(); - - let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); - let listen_port: u16 = 12323; - let tcp_request_timeout = Duration::from_secs(DEFAULT_TCP_REQUEST_TIMEOUT); - - let sock_addr = SocketAddr::new(ip_addr, listen_port); - let udp_socket = UdpSocket::bind(&sock_addr) - .await - .expect("could not bind udp socket"); - let tcp_listener = TcpListener::bind(&sock_addr) - .await - .expect("could not bind tcp listener"); - - let mut server = ServerFuture::new(dns_manager.catalog); - - // load all the listeners - info!("DNS server listening for UDP on {:?}", udp_socket); - server.register_socket(udp_socket); - - info!("DNS server listening for TCP on {:?}", tcp_listener); - server.register_listener(tcp_listener, tcp_request_timeout); - info!("awaiting DNS connections..."); - - server -} diff --git a/src/http.rs b/src/http.rs index 14e16e5..c258be9 100644 --- a/src/http.rs +++ b/src/http.rs @@ -8,43 +8,32 @@ */ use rocket_contrib::json::Json; use serde::Deserialize; +use std::thread; +use crate::client::check_domain_available; #[get("/")] -fn index() -> &'static str { +pub fn index() -> &'static str { "This is the peach-dyn-dns server." } #[derive(Deserialize, Debug)] -struct RegisterDomainPost { +pub struct RegisterDomainPost { domain: String, } #[post("/register-domain", data = "")] -fn register_domain(data: Json) -> &'static str { +pub async fn register_domain(data: Json) -> &'static str { info!("++ post request to register new domain: {:?}", data); - "New domain registered" // TODO: return secret -} - -#[derive(Deserialize, Debug)] -struct UpdateDomainPost { - domain: String, - secret: String, -} - -#[post("/update-domain", data = "")] -fn update_domain(data: Json) -> &'static str { - info!("++ post request to update domain: {:?}", data); - "Updating domain" // TODO: validate, then do it -} - -pub async fn server() { - - let rocket_result= rocket::build() - .mount("/", routes![index, register_domain, update_domain]) - .launch() - .await; - - if let Err(err) = rocket_result { - error!("++ error launching rocket server: {:?}", err); + // TODO: first confirm domain is in the right format ("*.dyn.peachcloud.org") + let handle = thread::spawn(move || { + let domain_already_exists = check_domain_available(&data.domain); + domain_already_exists + }); + let domain_already_exists = handle.join().unwrap(); + if domain_already_exists { + "can't register domain already exists" + } else { + // TODO: use bash to generate a tsig key, update bind config, and then return the secret + "New domain registered" } -} +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 690daa3..f1e681a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,33 +6,22 @@ extern crate rocket; use futures::try_join; use std::io; use tokio::task; +use crate::http::{index, register_domain}; mod cli; -mod dns; mod http; +mod client; #[tokio::main] async fn main() { - let _args = cli::args().expect("error parsing args"); - // create future for dns and http servers - let dns_future = task::spawn(dns::server()); - let http_future = task::spawn(http::server()); + let rocket_result= rocket::build() + .mount("/", routes![index, register_domain]) + .launch() + .await; - // join futures - let result = try_join!(dns_future, http_future); - - match result { - Err(e) => { - io::Error::new( - io::ErrorKind::Interrupted, - "Server stopping due to interruption", - ); - error!("server failure: {}", e); - } - Ok(_val) => { - info!("we're stopping for some unexpected reason"); - } + if let Err(err) = rocket_result { + error!("++ error launching rocket server: {:?}", err); } - info!("we're stopping for some unexpected reason"); + }