|
@ -1,5 +1,5 @@ |
|
|
use std::io::Read;
|
|
|
use std::io::Read;
|
|
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
|
|
|
|
|
|
|
|
use std::net::IpAddr;
|
|
|
use std::str::FromStr;
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
|
use actix_web::{AsyncResponder, FutureResponse, HttpMessage, HttpRequest, HttpResponse, Scope};
|
|
|
use actix_web::{AsyncResponder, FutureResponse, HttpMessage, HttpRequest, HttpResponse, Scope};
|
|
@ -10,12 +10,25 @@ use cloudflare::{Cloudflare, zones}; |
|
|
use futures::future::Future;
|
|
|
use futures::future::Future;
|
|
|
|
|
|
|
|
|
use crate::config::model::Config;
|
|
|
use crate::config::model::Config;
|
|
|
use crate::server::error::APIError;
|
|
|
|
|
|
|
|
|
use crate::server::error::{APIError, Error};
|
|
|
use crate::server::error::Result;
|
|
|
use crate::server::error::Result;
|
|
|
use crate::server::middleware::api_auth::APIAuthRootAndZone;
|
|
|
use crate::server::middleware::api_auth::APIAuthRootAndZone;
|
|
|
use crate::server::router::AppState;
|
|
|
use crate::server::router::AppState;
|
|
|
use crate::server::util;
|
|
|
use crate::server::util;
|
|
|
|
|
|
|
|
|
|
|
|
trait RecordType {
|
|
|
|
|
|
fn get_record_type(&self) -> zones::dns::RecordType;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl RecordType for IpAddr {
|
|
|
|
|
|
fn get_record_type(&self) -> zones::dns::RecordType {
|
|
|
|
|
|
match &self {
|
|
|
|
|
|
IpAddr::V4(_) => zones::dns::RecordType::A,
|
|
|
|
|
|
IpAddr::V6(_) => zones::dns::RecordType::AAAA
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn route(scope: Scope<AppState>) -> Scope<AppState> {
|
|
|
pub fn route(scope: Scope<AppState>) -> Scope<AppState> {
|
|
|
scope
|
|
|
scope
|
|
|
.resource("address", |r| r.method(Method::GET).f(get_request_address))
|
|
|
.resource("address", |r| r.method(Method::GET).f(get_request_address))
|
|
@ -52,18 +65,55 @@ fn parse_remote_info(remote_info: &str) -> Result<IpAddr> { |
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn get_request_address(req: &HttpRequest<AppState>) -> HttpResponse {
|
|
|
|
|
|
|
|
|
fn determine_request_address(req: &HttpRequest<AppState>) -> Result<Option<IpAddr>> {
|
|
|
match req.connection_info().remote() {
|
|
|
match req.connection_info().remote() {
|
|
|
Some(remote_info) => match parse_remote_info(remote_info) {
|
|
|
Some(remote_info) => match parse_remote_info(remote_info) {
|
|
|
Ok(addr) => HttpResponse::build(StatusCode::OK)
|
|
|
|
|
|
|
|
|
Ok(addr) => Ok(Some(addr)),
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
error!("{:?}", e);
|
|
|
|
|
|
Err(Error::from(e))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
None => Ok(None),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_request_address(req: &HttpRequest<AppState>) -> HttpResponse {
|
|
|
|
|
|
match determine_request_address(&req) {
|
|
|
|
|
|
Ok(potential_addr) => match potential_addr {
|
|
|
|
|
|
Some(addr) => HttpResponse::build(StatusCode::OK)
|
|
|
.content_type("text/plain")
|
|
|
.content_type("text/plain")
|
|
|
.body(format!("{}", addr)),
|
|
|
.body(format!("{}", addr)),
|
|
|
|
|
|
None => HttpResponse::build(StatusCode::BAD_REQUEST).finish()
|
|
|
|
|
|
},
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::build(StatusCode::BAD_REQUEST).finish()
|
|
|
HttpResponse::build(StatusCode::BAD_REQUEST).finish()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
None => HttpResponse::build(StatusCode::BAD_REQUEST).finish(),
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn find_dns_record(
|
|
|
|
|
|
cloudflare: &Cloudflare,
|
|
|
|
|
|
zone_id: &str,
|
|
|
|
|
|
record_type: zones::dns::RecordType,
|
|
|
|
|
|
record_name: &str,
|
|
|
|
|
|
) -> Result<Option<zones::dns::DnsRecord>> {
|
|
|
|
|
|
match zones::dns::list_dns_of_type(cloudflare, &zone_id, record_type) {
|
|
|
|
|
|
Ok(dns_entries) => {
|
|
|
|
|
|
debug!("Looking for dns record: {}", record_name);
|
|
|
|
|
|
for dns_entry in dns_entries {
|
|
|
|
|
|
debug!("record {:?}", dns_entry);
|
|
|
|
|
|
if dns_entry.name == record_name {
|
|
|
|
|
|
return Ok(Some(dns_entry));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Ok(None)
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
error!("{:?}", e);
|
|
|
|
|
|
Err(Error::from(e))
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -78,30 +128,26 @@ fn get_address(req: &HttpRequest<AppState>) -> HttpResponse { |
|
|
let root = root_match.unwrap();
|
|
|
let root = root_match.unwrap();
|
|
|
let zone = zone_match.unwrap();
|
|
|
let zone = zone_match.unwrap();
|
|
|
match util::get_domain_from_root(&root) {
|
|
|
match util::get_domain_from_root(&root) {
|
|
|
Some(domain) => match zones::get_zoneid(cloudflare, &domain) {
|
|
|
|
|
|
Ok(zone_id) => match zones::dns::list_dns_of_type(cloudflare, &zone_id, zones::dns::RecordType::A) {
|
|
|
|
|
|
Ok(dns_entries) => {
|
|
|
|
|
|
let record_name = format!("{}.{}", &zone, &root);
|
|
|
|
|
|
debug!("Looking for dns record: {}", record_name);
|
|
|
|
|
|
for dns_entry in dns_entries {
|
|
|
|
|
|
debug!("record {:?}", dns_entry);
|
|
|
|
|
|
if dns_entry.name == record_name {
|
|
|
|
|
|
return HttpResponse::build(StatusCode::OK)
|
|
|
|
|
|
.content_type("text/plain")
|
|
|
|
|
|
.body(format!("{}", dns_entry.content));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
HttpResponse::NotFound().into()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Some(domain) => {
|
|
|
|
|
|
let zone_id = match zones::get_zoneid(cloudflare, &domain) {
|
|
|
|
|
|
Ok(zone_id) => zone_id,
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
|
|
|
|
|
|
return HttpResponse::BadRequest().into();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
match find_dns_record(cloudflare, &zone_id, zones::dns::RecordType::A, &format!("{}.{}", &zone, &root)) {
|
|
|
|
|
|
Ok(record) => match record {
|
|
|
|
|
|
Some(dns_entry) => HttpResponse::build(StatusCode::OK)
|
|
|
|
|
|
.content_type("text/plain")
|
|
|
|
|
|
.body(format!("{}", dns_entry.content)),
|
|
|
|
|
|
None => HttpResponse::NotFound().into()
|
|
|
},
|
|
|
},
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
None => HttpResponse::BadRequest().into()
|
|
|
None => HttpResponse::BadRequest().into()
|
|
|
}
|
|
|
}
|
|
@ -118,28 +164,37 @@ fn update_address_automatically(req: &HttpRequest<AppState>) -> HttpResponse { |
|
|
} else {
|
|
|
} else {
|
|
|
let root = root_match.unwrap();
|
|
|
let root = root_match.unwrap();
|
|
|
let zone = zone_match.unwrap();
|
|
|
let zone = zone_match.unwrap();
|
|
|
match util::get_domain_from_root(&root) {
|
|
|
|
|
|
Some(domain) => match zones::get_zoneid(cloudflare, &domain) {
|
|
|
|
|
|
Ok(zone_id) => match zones::dns::list_dns_of_type(cloudflare, &zone_id, zones::dns::RecordType::A) {
|
|
|
|
|
|
Ok(dns_entries) => {
|
|
|
|
|
|
|
|
|
let request_address = match determine_request_address(&req) {
|
|
|
|
|
|
Ok(potential_addr) => match potential_addr {
|
|
|
|
|
|
Some(addr) => addr,
|
|
|
|
|
|
None => return HttpResponse::build(StatusCode::BAD_REQUEST).finish()
|
|
|
|
|
|
},
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
error!("{:?}", e);
|
|
|
|
|
|
return HttpResponse::build(StatusCode::BAD_REQUEST).finish();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
let request_address_content = format!("{}", request_address);
|
|
|
let record_name = format!("{}.{}", &zone, &root);
|
|
|
let record_name = format!("{}.{}", &zone, &root);
|
|
|
debug!("Looking for dns record: {}", record_name);
|
|
|
|
|
|
for dns_entry in dns_entries {
|
|
|
|
|
|
debug!("record {:?}", dns_entry);
|
|
|
|
|
|
if dns_entry.name == record_name {
|
|
|
|
|
|
return match req.connection_info().remote() {
|
|
|
|
|
|
Some(remote_info) => match parse_remote_info(remote_info) {
|
|
|
|
|
|
Ok(addr) => match addr {
|
|
|
|
|
|
IpAddr::V4(_) => match zones::dns::create_dns_entry(cloudflare, &zone_id, zones::dns::RecordType::A, &record_name, &format!("{}", addr)) {
|
|
|
|
|
|
Ok(result_record) => HttpResponse::build(StatusCode::OK)
|
|
|
|
|
|
.content_type("text/plain")
|
|
|
|
|
|
.body(format!("{}", result_record.content)),
|
|
|
|
|
|
|
|
|
match util::get_domain_from_root(&root) {
|
|
|
|
|
|
Some(domain) => {
|
|
|
|
|
|
let zone_id = match zones::get_zoneid(cloudflare, &domain) {
|
|
|
|
|
|
Ok(zone_id) => zone_id,
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
|
|
|
|
|
|
return HttpResponse::BadRequest().into();
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
IpAddr::V6(_) => match zones::dns::create_dns_entry(cloudflare, &zone_id, zones::dns::RecordType::AAAA, &record_name, &format!("{}", addr)) {
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
match find_dns_record(cloudflare, &zone_id, request_address.get_record_type(), &record_name) {
|
|
|
|
|
|
Ok(record) => match record {
|
|
|
|
|
|
Some(dns_entry) => {
|
|
|
|
|
|
match zones::dns::update_dns_entry(
|
|
|
|
|
|
cloudflare,
|
|
|
|
|
|
&dns_entry.zone_id,
|
|
|
|
|
|
&dns_entry.id,
|
|
|
|
|
|
request_address.get_record_type(),
|
|
|
|
|
|
&dns_entry.name,
|
|
|
|
|
|
&request_address_content) {
|
|
|
Ok(result_record) => HttpResponse::build(StatusCode::OK)
|
|
|
Ok(result_record) => HttpResponse::build(StatusCode::OK)
|
|
|
.content_type("text/plain")
|
|
|
.content_type("text/plain")
|
|
|
.body(format!("{}", result_record.content)),
|
|
|
.body(format!("{}", result_record.content)),
|
|
@ -148,27 +203,27 @@ fn update_address_automatically(req: &HttpRequest<AppState>) -> HttpResponse { |
|
|
HttpResponse::InternalServerError().into()
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
error!("{:?}", e);
|
|
|
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
None => HttpResponse::BadRequest().into()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
HttpResponse::BadRequest().into()
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
None => match zones::dns::create_dns_entry(
|
|
|
|
|
|
cloudflare,
|
|
|
|
|
|
&zone_id,
|
|
|
|
|
|
request_address.get_record_type(),
|
|
|
|
|
|
&record_name,
|
|
|
|
|
|
&request_address_content) {
|
|
|
|
|
|
Ok(result_record) => HttpResponse::build(StatusCode::OK)
|
|
|
|
|
|
.content_type("text/plain")
|
|
|
|
|
|
.body(format!("{}", result_record.content)),
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
Err(e) => {
|
|
|
Err(e) => {
|
|
|
error!("{:?}", e);
|
|
|
error!("{:?}", e);
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
HttpResponse::InternalServerError().into()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
},
|
|
|
},
|
|
|
None => HttpResponse::BadRequest().into()
|
|
|
None => HttpResponse::BadRequest().into()
|
|
|
}
|
|
|
}
|
|
|