Working json response with keyfile
This commit is contained in:
parent
7c626c5ef7
commit
4d14a58ff7
|
@ -1,3 +1,4 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
ns_tests/*.key
|
||||
ns_tests/*
|
||||
|
|
|
@ -23,15 +23,6 @@ serde = "1.0.125"
|
|||
dotenv = "0.15.0"
|
||||
tera = "1"
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "client"
|
||||
path = "src/client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "zone"
|
||||
path = "src/generate_zone.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "main"
|
||||
path = "src/main.rs"
|
|
@ -1,57 +0,0 @@
|
|||
use futures::try_join;
|
||||
use std::io;
|
||||
use tokio::task;
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
use trust_dns_client::client::{Client, SyncClient};
|
||||
use trust_dns_client::op::update_message;
|
||||
use trust_dns_client::op::DnsResponse;
|
||||
use trust_dns_client::rr::{DNSClass, Name, RData, Record, RecordSet, RecordType};
|
||||
use trust_dns_client::tcp::TcpClientConnection;
|
||||
use trust_dns_client::udp::UdpClientConnection;
|
||||
use trust_dns_server::authority::{
|
||||
AuthLookup, Authority, LookupError, MessageRequest, UpdateResult,
|
||||
};
|
||||
|
||||
pub fn check_domain_available_using_nslookup(domain: &str) -> bool {
|
||||
let address = "167.99.136.83:53".parse().unwrap();
|
||||
let conn = UdpClientConnection::new(address).unwrap();
|
||||
let client = SyncClient::new(conn);
|
||||
let name = Name::from_str(domain).unwrap();
|
||||
|
||||
println!("++ making query {:?}", domain);
|
||||
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::A).unwrap();
|
||||
println!("++ received response");
|
||||
let answers: &[Record] = response.answers();
|
||||
|
||||
if answers.len() > 0 {
|
||||
println!("found: {:?}", answers[0].rdata());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn update_test() {
|
||||
let address = "167.99.136.83:53".parse().unwrap();
|
||||
let conn = UdpClientConnection::new(address).unwrap();
|
||||
let client = SyncClient::new(conn);
|
||||
|
||||
// Specify the name, note the final '.' which specifies it's an FQDN
|
||||
let name = Name::from_str("test.time.commoninternet.net").unwrap();
|
||||
|
||||
let record = Record::from_rdata(name.clone(), 8, RData::A(Ipv4Addr::new(127, 0, 0, 10)));
|
||||
let rrset: RecordSet = record.clone().into();
|
||||
let zone_origin = Name::from_str("time.commoninternet.net").unwrap();
|
||||
|
||||
let response: DnsResponse = client
|
||||
.create(rrset, zone_origin)
|
||||
.expect("failed to create record");
|
||||
println!("response: {:?}", response);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
check_domain_available_using_nslookup("test");
|
||||
// update_test();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
// this is the base domain which peach-dyndns creates subdomains under
|
||||
// e.g. blue.dyn.peachcloud.org
|
||||
pub const BASE_DOMAIN: &str = "dyn.commoninternet.net";
|
|
@ -0,0 +1,22 @@
|
|||
use std::string::FromUtf8Error;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PeachDynError {
|
||||
GenerateTsigIoError(std::io::Error),
|
||||
GenerateTsigParseError(std::string::FromUtf8Error),
|
||||
DomainAlreadyExistsError(String),
|
||||
BindConfigurationError(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for PeachDynError {
|
||||
fn from(err: std::io::Error) -> PeachDynError {
|
||||
PeachDynError::GenerateTsigIoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for PeachDynError {
|
||||
fn from(err: std::string::FromUtf8Error) -> PeachDynError {
|
||||
PeachDynError::GenerateTsigParseError(err)
|
||||
}
|
||||
}
|
|
@ -8,33 +8,12 @@ use std::fs::File;
|
|||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use std::string::FromUtf8Error;
|
||||
use tera::{Tera, Context};
|
||||
use crate::errors::PeachDynError;
|
||||
use crate::constants::BASE_DOMAIN;
|
||||
|
||||
|
||||
const BASE_DOMAIN: &str = "dyn.commoninternet.net";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PeachDynError {
|
||||
GenerateTsigIoError(std::io::Error),
|
||||
GenerateTsigParseError(std::string::FromUtf8Error),
|
||||
DomainAlreadyExistsError(String),
|
||||
BindConfigurationError(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for PeachDynError {
|
||||
fn from(err: std::io::Error) -> PeachDynError {
|
||||
PeachDynError::GenerateTsigIoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for PeachDynError {
|
||||
fn from(err: std::string::FromUtf8Error) -> PeachDynError {
|
||||
PeachDynError::GenerateTsigParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// helper function to generate the text of a TSIG key file
|
||||
/// function to generate the text of a TSIG key file
|
||||
pub fn generate_tsig_key(full_domain: &str) -> Result<String, PeachDynError> {
|
||||
let output = Command::new("/usr/sbin/tsig-keygen")
|
||||
.arg("-a")
|
||||
|
@ -45,7 +24,8 @@ pub fn generate_tsig_key(full_domain: &str) -> Result<String, PeachDynError> {
|
|||
Ok(key_file_text)
|
||||
}
|
||||
|
||||
/// helper function which returns true if all three conditions are true
|
||||
/// function which helps us guarantee that a given domain is not already being used by bind
|
||||
/// it checks three places for the domain, and only returns true if it is not found in all three places
|
||||
/// - no already extant tsig key for the given domain
|
||||
/// - no zone file for the given domain in /var/lib/bind
|
||||
/// - no zone section for the given domain in named.conf.local
|
||||
|
@ -64,6 +44,7 @@ pub fn check_domain_available(full_domain: &str) -> bool {
|
|||
|
||||
// domain is only available if domain does not exist in either named.conf.local or dyn.commoninternet.netkeys
|
||||
// and a file with that name is not found in /var/lib/bind/
|
||||
// grep returns a status code of 1 if lines are not found, which is why we check that the codes equal 1
|
||||
let domain_available = (code1 == 1) & (code2 == 1) & (!condition3);
|
||||
|
||||
// return
|
||||
|
@ -71,9 +52,9 @@ pub fn check_domain_available(full_domain: &str) -> bool {
|
|||
|
||||
}
|
||||
|
||||
/// helper function which generates all necessary bind configuration to serve the given
|
||||
/// function which generates all necessary bind configuration to serve the given
|
||||
/// subdomain using dynamic DNS authenticated via a new TSIG key which is unique to that subdomain
|
||||
/// and thus only the possessor of that key can use nsupdate to modify the records
|
||||
/// - thus only the possessor of that key can use nsupdate to modify the records
|
||||
/// for that subodmain
|
||||
pub fn generate_zone(full_domain: &str) -> Result<String, PeachDynError> {
|
||||
|
||||
|
@ -152,18 +133,3 @@ pub fn generate_zone(full_domain: &str) -> Result<String, PeachDynError> {
|
|||
// return success
|
||||
Ok(key_file_text)
|
||||
}
|
||||
|
||||
// this main function is only used for the purpose of testing
|
||||
fn main() {
|
||||
|
||||
let result = generate_zone("purple");
|
||||
match result {
|
||||
Ok(key_text) => {
|
||||
println!("successfully created zone");
|
||||
println!("{}", key_text);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("error creating zone {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
43
src/http.rs
43
src/http.rs
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* /register-user (sends an email verification to create a new account)
|
||||
* /verify (for clicking the link in the email)
|
||||
* /register-domain (add a new domain and get back the secret for subsequent updating)
|
||||
* /update-domain (update the IP for the domain, passing the associated secret)
|
||||
*
|
||||
*/
|
||||
use crate::generate_zone::{check_domain_available, generate_zone};
|
||||
use rocket_contrib::json::{Json, JsonValue};
|
||||
use serde::Deserialize;
|
||||
use std::thread;
|
||||
|
||||
#[get("/")]
|
||||
pub fn index() -> &'static str {
|
||||
"This is the peach-dyn-dns server."
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct RegisterDomainPost {
|
||||
domain: String,
|
||||
}
|
||||
|
||||
#[post("/register-domain", data = "<data>")]
|
||||
pub async fn register_domain(data: Json<RegisterDomainPost>) -> &'static str {
|
||||
info!("++ post request to register new domain: {:?}", data);
|
||||
// TODO: first confirm domain is in the right format ("*.dyn.peachcloud.org")
|
||||
let is_domain_available = check_domain_available(&data.domain);
|
||||
if !is_domain_available{
|
||||
"can't register domain that already exists"
|
||||
} else {
|
||||
let result = generate_zone(&data.domain);
|
||||
match result {
|
||||
Ok(key_file_text) => {
|
||||
// TODO: figure out how to return key_file_text
|
||||
"successfully created zone"
|
||||
}
|
||||
Err(err) => {
|
||||
"there was an error registering the domain"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,14 +3,15 @@
|
|||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use crate::http::{index, register_domain};
|
||||
use crate::routes::{index, register_domain};
|
||||
use futures::try_join;
|
||||
use std::io;
|
||||
use tokio::task;
|
||||
|
||||
mod cli;
|
||||
mod client;
|
||||
mod http;
|
||||
mod routes;
|
||||
mod errors;
|
||||
mod constants;
|
||||
mod generate_zone;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
*
|
||||
* /register-user (sends an email verification to create a new account) NOT IMPLEMENTED
|
||||
* /verify (for clicking the link in the email) NOT IMPLEMENTED
|
||||
* /register-domain (add a new domain and get back the TSIG key for subsequent updating with nsupdate)
|
||||
*/
|
||||
use crate::generate_zone::{check_domain_available, generate_zone};
|
||||
use rocket_contrib::json::{Json, JsonValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[get("/")]
|
||||
pub fn index() -> &'static str {
|
||||
"This is the peach-dyndns server."
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct JsonResponse {
|
||||
pub status: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<JsonValue>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub msg: Option<String>,
|
||||
}
|
||||
|
||||
// helper function to build a JsonResponse object
|
||||
pub fn build_json_response(
|
||||
status: String,
|
||||
data: Option<JsonValue>,
|
||||
msg: Option<String>,
|
||||
) -> JsonResponse {
|
||||
JsonResponse { status, data, msg }
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct RegisterDomainPost {
|
||||
domain: String,
|
||||
}
|
||||
|
||||
#[post("/register-domain", data = "<data>")]
|
||||
pub async fn register_domain(data: Json<RegisterDomainPost>) -> Json<JsonResponse> {
|
||||
info!("++ post request to register new domain: {:?}", data);
|
||||
// TODO: first confirm domain is in the right format ("*.dyn.peachcloud.org")
|
||||
let is_domain_available = check_domain_available(&data.domain);
|
||||
if !is_domain_available{
|
||||
let status = "error".to_string();
|
||||
let msg = "can't register a domain that is already registered".to_string();
|
||||
Json(build_json_response(status, None, Some(msg)))
|
||||
} else {
|
||||
let result = generate_zone(&data.domain);
|
||||
match result {
|
||||
Ok(key_file_text) => {
|
||||
let status = "success".to_string();
|
||||
let msg = key_file_text.to_string();
|
||||
Json(build_json_response(status, None, Some(msg)))
|
||||
}
|
||||
Err(_err) => {
|
||||
let status = "error".to_string();
|
||||
let msg = "there was an error creating the zone file".to_string();
|
||||
Json(build_json_response(status, None, Some(msg)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue