@ -1,13 +1,40 @@
use std ::io ::Read ;
use std ::net ::IpAddr ;
use std ::str ::FromStr ;
use actix_web ::{ AsyncResponder , FutureResponse , HttpMessage , HttpRequest , HttpResponse , Scope } ;
use actix_web ::http ::Method ;
use actix_web ::http ::Method ;
use actix_web ::http ::StatusCode ;
use actix_web ::http ::StatusCode ;
use actix_web ::{ AsyncResponder , FutureResponse , HttpMessage , HttpRequest , HttpResponse , Scope } ;
use bytes ::{ Buf , Bytes , IntoBuf } ;
use bytes ::{ Buf , Bytes , IntoBuf } ;
use cloudflare ::{ Cloudflare , zones } ;
use futures ::future ::Future ;
use futures ::future ::Future ;
use std ::io ::Read ;
pub fn route ( scope : Scope < ( ) > ) -> Scope < ( ) > {
scope . nested ( "{root}/{zone}" , | zone_scope | {
use crate ::config ::model ::Config ;
use crate ::server ::error ::{ APIError , Error } ;
use crate ::server ::error ::Result ;
use crate ::server ::middleware ::api_auth ::APIAuthRootAndZone ;
use crate ::server ::router ::AppState ;
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 > {
scope
. resource ( "address" , | r | r . method ( Method ::GET ) . f ( get_request_address ) )
. nested ( "{root}/{zone}" , | zone_scope | {
zone_scope
zone_scope
. middleware ( APIAuthRootAndZone )
. resource ( "" , | r | r . method ( Method ::GET ) . f ( get_address ) )
. resource ( "" , | r | r . method ( Method ::GET ) . f ( get_address ) )
. resource ( "update" , | r | {
. resource ( "update" , | r | {
r . method ( Method ::GET ) . f ( update_address_automatically ) ;
r . method ( Method ::GET ) . f ( update_address_automatically ) ;
@ -21,25 +48,190 @@ fn update_address(address: String) -> String {
address
address
}
}
fn get_address ( req : & HttpRequest ) -> HttpResponse {
fn parse_remote_info ( remote_info : & str ) -> Result < IpAddr > {
let mut remote_address = String ::from ( remote_info ) ;
if remote_address . contains ( ':' ) {
let last_colon_index = remote_address . rfind ( ':' ) . unwrap ( ) ;
let _port = remote_address . split_off ( last_colon_index ) ;
if remote_address . starts_with ( '[' ) & & remote_address . ends_with ( ']' ) {
remote_address = String ::from ( remote_address . trim_matches ( | c | c = = '[' | | c = = ']' ) )
}
match IpAddr ::from_str ( & remote_address ) {
Ok ( v ) = > Ok ( v ) ,
Err ( e ) = > Err ( APIError ::new ( & format ! ( "Address Parse Error \"{}\"" , remote_address ) , Some ( Box ::from ( e ) ) ) )
}
} else {
match IpAddr ::from_str ( & remote_address ) {
Ok ( v ) = > Ok ( v ) ,
Err ( e ) = > Err ( APIError ::new ( & format ! ( "Address Parse Error \"{}\"" , remote_address ) , Some ( Box ::from ( e ) ) ) )
}
}
}
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 ) {
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 )
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 ( ) ,
None = > HttpResponse ::build ( StatusCode ::BAD_REQUEST ) . finish ( )
} ,
Err ( e ) = > {
error ! ( "{:?}" , e ) ;
HttpResponse ::build ( StatusCode ::BAD_REQUEST ) . finish ( )
}
}
}
}
}
fn update_address_automatically ( req : & HttpRequest ) -> HttpResponse {
match req . connection_info ( ) . remote ( ) {
Some ( addr ) = > HttpResponse ::build ( StatusCode ::OK )
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 ) )
}
}
}
fn get_address ( req : & HttpRequest < AppState > ) -> HttpResponse {
let root_match = util ::get_match_value ( req , "root" ) ;
let zone_match = util ::get_match_value ( req , "zone" ) ;
let cloudflare : & Cloudflare = & req . state ( ) . cloudflare ;
if root_match . is_none ( ) | | zone_match . is_none ( ) {
HttpResponse ::BadRequest ( ) . into ( )
} else {
let root = root_match . unwrap ( ) ;
let zone = zone_match . unwrap ( ) ;
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 ) = > {
error ! ( "{:?}" , e ) ;
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" )
. content_type ( "text/plain" )
. body ( format ! ( "{}" , addr ) ) ,
None = > HttpResponse ::build ( StatusCode ::BAD_REQUEST ) . finish ( ) ,
. body ( format ! ( "{}" , dns_entry . content ) ) ,
None = > HttpResponse ::NotFound ( ) . into ( )
} ,
Err ( e ) = > {
error ! ( "{:?}" , e ) ;
HttpResponse ::InternalServerError ( ) . into ( )
}
}
} ,
None = > HttpResponse ::BadRequest ( ) . into ( )
}
}
}
fn update_address_automatically ( req : & HttpRequest < AppState > ) -> HttpResponse {
let root_match = util ::get_match_value ( req , "root" ) ;
let zone_match = util ::get_match_value ( req , "zone" ) ;
let cloudflare : & Cloudflare = & req . state ( ) . cloudflare ;
if root_match . is_none ( ) | | zone_match . is_none ( ) {
HttpResponse ::BadRequest ( ) . into ( )
} else {
let root = root_match . unwrap ( ) ;
let zone = zone_match . unwrap ( ) ;
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 ) ;
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 ) = > {
error ! ( "{:?}" , e ) ;
return HttpResponse ::BadRequest ( ) . into ( ) ;
}
} ;
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 )
. content_type ( "text/plain" )
. body ( format ! ( "{}" , result_record . content ) ) ,
Err ( e ) = > {
error ! ( "{:?}" , e ) ;
HttpResponse ::InternalServerError ( ) . 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 ) = > {
error ! ( "{:?}" , e ) ;
HttpResponse ::InternalServerError ( ) . into ( )
}
}
} ,
Err ( e ) = > {
error ! ( "{:?}" , e ) ;
HttpResponse ::InternalServerError ( ) . into ( )
}
}
} ,
None = > HttpResponse ::BadRequest ( ) . into ( )
}
}
}
}
}
fn update_address_manually ( req : & HttpRequest ) -> FutureResponse < HttpResponse > {
fn update_address_manually ( req : & HttpRequest < AppState > ) -> FutureResponse < HttpResponse > {
req . body ( )
req . body ( )
. limit ( 48 )
. limit ( 48 )
. from_err ( )
. from_err ( )