Using nsupdate and bind
This commit is contained in:
parent
f4d6b31125
commit
6368303922
|
@ -28,5 +28,5 @@ name = "client"
|
||||||
path = "src/client.rs"
|
path = "src/client.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "dns"
|
name = "main"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
|
@ -1,8 +1,3 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate rocket;
|
|
||||||
|
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use std::io;
|
use std::io;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
@ -21,33 +16,23 @@ use trust_dns_server::authority::{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
fn simple_test() {
|
pub fn check_domain_available(domain: &str) -> bool {
|
||||||
// let address = "127.0.0.1:12323".parse().unwrap();
|
|
||||||
let address = "167.99.136.83:53".parse().unwrap();
|
let address = "167.99.136.83:53".parse().unwrap();
|
||||||
let conn = UdpClientConnection::new(address).unwrap();
|
let conn = UdpClientConnection::new(address).unwrap();
|
||||||
let client = SyncClient::new(conn);
|
let client = SyncClient::new(conn);
|
||||||
// let conn = TcpClientConnection::new(address).unwrap();
|
let name = Name::from_str(domain).unwrap();
|
||||||
// let client = SyncClient::new(conn);
|
|
||||||
|
|
||||||
// Specify the name, note the final '.' which specifies it's an FQDN
|
info!("++ making query {:?}", domain);
|
||||||
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");
|
|
||||||
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap();
|
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap();
|
||||||
println!("++ received response");
|
info!("++ 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
|
|
||||||
let answers: &[Record] = response.answers();
|
let answers: &[Record] = response.answers();
|
||||||
|
|
||||||
// Records are generic objects which can contain any data.
|
if answers.len() > 0 {
|
||||||
// In order to access it we need to first check what type of record it is
|
info!("found: {:?}", answers[0].rdata());
|
||||||
// In this case we are interested in A, IPv4 address
|
true
|
||||||
println!("found: {:?}", answers[0].rdata())
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_test() {
|
fn update_test() {
|
||||||
|
@ -70,6 +55,6 @@ fn update_test() {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
// simple_test();
|
check_domain_available("test");
|
||||||
update_test();
|
// update_test();
|
||||||
}
|
}
|
||||||
|
|
172
src/dns.rs
172
src/dns.rs
|
@ -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<RrKey, RecordSet> {
|
|
||||||
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<Catalog> {
|
|
||||||
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
|
|
||||||
}
|
|
45
src/http.rs
45
src/http.rs
|
@ -8,43 +8,32 @@
|
||||||
*/
|
*/
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::thread;
|
||||||
|
use crate::client::check_domain_available;
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
fn index() -> &'static str {
|
pub fn index() -> &'static str {
|
||||||
"This is the peach-dyn-dns server."
|
"This is the peach-dyn-dns server."
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct RegisterDomainPost {
|
pub struct RegisterDomainPost {
|
||||||
domain: String,
|
domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/register-domain", data = "<data>")]
|
#[post("/register-domain", data = "<data>")]
|
||||||
fn register_domain(data: Json<RegisterDomainPost>) -> &'static str {
|
pub async fn register_domain(data: Json<RegisterDomainPost>) -> &'static str {
|
||||||
info!("++ post request to register new domain: {:?}", data);
|
info!("++ post request to register new domain: {:?}", data);
|
||||||
"New domain registered" // TODO: return secret
|
// 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);
|
||||||
#[derive(Deserialize, Debug)]
|
domain_already_exists
|
||||||
struct UpdateDomainPost {
|
});
|
||||||
domain: String,
|
let domain_already_exists = handle.join().unwrap();
|
||||||
secret: String,
|
if domain_already_exists {
|
||||||
}
|
"can't register domain already exists"
|
||||||
|
} else {
|
||||||
#[post("/update-domain", data = "<data>")]
|
// TODO: use bash to generate a tsig key, update bind config, and then return the secret
|
||||||
fn update_domain(data: Json<UpdateDomainPost>) -> &'static str {
|
"New domain registered"
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
29
src/main.rs
29
src/main.rs
|
@ -6,33 +6,22 @@ extern crate rocket;
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use std::io;
|
use std::io;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
use crate::http::{index, register_domain};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod dns;
|
|
||||||
mod http;
|
mod http;
|
||||||
|
mod client;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let _args = cli::args().expect("error parsing args");
|
|
||||||
|
|
||||||
// create future for dns and http servers
|
let rocket_result= rocket::build()
|
||||||
let dns_future = task::spawn(dns::server());
|
.mount("/", routes![index, register_domain])
|
||||||
let http_future = task::spawn(http::server());
|
.launch()
|
||||||
|
.await;
|
||||||
|
|
||||||
// join futures
|
if let Err(err) = rocket_result {
|
||||||
let result = try_join!(dns_future, http_future);
|
error!("++ error launching rocket server: {:?}", err);
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
info!("we're stopping for some unexpected reason");
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue