2019-09-17 15:34:36 +00:00
mod file ;
2019-09-18 09:12:50 +00:00
mod network ;
2019-09-17 15:34:36 +00:00
2020-03-22 21:10:50 +00:00
use file ::{ read_file , write_file } ;
2019-09-18 09:12:50 +00:00
use human_panic ::setup_panic ;
use network ::{ get_current_ip , get_dns_record_id , get_zone_identifier , update_ddns } ;
2019-09-17 15:34:36 +00:00
use quicli ::prelude ::* ;
2019-09-18 09:12:50 +00:00
use std ::path ::PathBuf ;
2019-09-17 15:34:36 +00:00
use structopt ::StructOpt ;
2020-03-22 21:10:50 +00:00
use anyhow ::{ Context , Result } ;
2019-09-17 15:34:36 +00:00
2019-09-23 08:58:41 +00:00
#[ derive(Deserialize) ]
struct Config {
email : String ,
auth_key : String ,
zone : String ,
domain : String ,
}
2019-09-17 15:34:36 +00:00
#[ derive(Debug, StructOpt) ]
/// Inform Cloudflare's DDNS service of the current IP address for your domain
struct Cli {
2019-09-23 08:58:41 +00:00
/// Your TOML config file containing all the required options (email, auth_key, zone, domain) which you can use instead of passing the arguments to the command line
#[ structopt(long = " config " , short = " f " ) ]
config : Option < PathBuf > ,
2019-09-17 15:34:36 +00:00
/// Your Cloudflare login email
2019-09-23 08:58:41 +00:00
#[ structopt(long = " email " , short = " e " , required_unless = " config " ) ]
email : Option < String > ,
2019-09-17 15:34:36 +00:00
/// The auth key you need to generate in your Cloudflare profile
2019-09-23 08:58:41 +00:00
#[ structopt(long = " key " , short = " k " , required_unless = " config " ) ]
auth_key : Option < String > ,
2019-09-17 15:34:36 +00:00
2019-09-18 08:15:11 +00:00
/// The zone in which your domain is (usually that is your base domain name)
2019-09-23 08:58:41 +00:00
#[ structopt(long = " zone " , short = " z " , required_unless = " config " ) ]
zone : Option < String > ,
2019-09-17 15:34:36 +00:00
/// The domain for which you want to report the current IP address
2019-09-23 08:58:41 +00:00
#[ structopt(long = " domain " , short = " d " , required_unless = " config " ) ]
domain : Option < String > ,
2019-09-17 15:34:36 +00:00
/// Cache file for previously reported IP address (if skipped the IP will be reported on every execution)
#[ structopt(long = " cache " , short = " c " ) ]
cache : Option < PathBuf > ,
}
2020-03-22 21:10:50 +00:00
fn main ( ) -> Result < ( ) > {
2019-09-17 15:34:36 +00:00
setup_panic! ( ) ;
let args = Cli ::from_args ( ) ;
2019-09-17 19:43:17 +00:00
2019-09-23 08:58:41 +00:00
let should_use_cache = args . cache . is_some ( ) ;
2019-09-17 19:43:17 +00:00
let cached_ip : Option < String > = match args . cache . clone ( ) {
Some ( v ) = > {
if v . exists ( ) {
2020-03-22 21:10:50 +00:00
Some ( read_file ( & v . clone ( ) ) . context ( " Could not read cache file " ) ? )
2019-09-17 19:43:17 +00:00
} else {
Some ( " 0.0.0.0 " . to_owned ( ) )
}
2019-09-18 09:12:50 +00:00
}
2019-09-17 19:43:17 +00:00
None = > None ,
} ;
let current_ip = get_current_ip ( ) ? ;
if cached_ip . is_some ( ) & & current_ip = = cached_ip . unwrap ( ) {
println! ( " IP is unchanged. Exiting... " ) ;
return Ok ( ( ) ) ;
}
if should_use_cache {
2019-09-18 09:12:50 +00:00
println! (
" Saving current IP {} to cache file {:?}... " ,
& current_ip ,
& args . cache . clone ( ) . unwrap ( )
) ;
2020-03-22 21:10:50 +00:00
write_file ( & args . cache . unwrap ( ) , & current_ip ) ? ;
2019-09-17 19:43:17 +00:00
}
2019-09-23 08:58:41 +00:00
let ( email , auth_key , zone , domain ) = match args . config {
Some ( c ) = > {
2020-03-22 21:10:50 +00:00
let config_str = read_file ( & c ) ? ;
2019-09-23 08:58:41 +00:00
let config : Config = toml ::from_str ( & config_str ) ? ;
( config . email , config . auth_key , config . zone , config . domain )
}
None = > (
args . email . expect ( " Email is not set " ) ,
args . auth_key . expect ( " Auth key is not set " ) ,
args . zone . expect ( " Zone is not set " ) ,
args . domain . expect ( " Domain is not set " ) ,
) ,
} ;
update ( & current_ip , & email , & auth_key , & zone , & domain ) ? ;
println! (
" Successfully updated the A record for {} to {} " ,
& domain , & current_ip
) ;
Ok ( ( ) )
}
fn update (
current_ip : & str ,
email : & str ,
auth_key : & str ,
zone : & str ,
domain : & str ,
2020-03-22 21:10:50 +00:00
) -> Result < ( ) > {
let zone_id = get_zone_identifier ( & zone , & email , & auth_key ) . context ( " Error getting the zone identifier " ) ? ;
let record_id = get_dns_record_id ( & zone_id , & domain , & email , & auth_key ) . context ( " Error getting the DNS record ID " ) ? ;
2019-09-17 19:43:17 +00:00
2019-09-18 09:12:50 +00:00
update_ddns (
& current_ip ,
2019-09-23 08:58:41 +00:00
& domain ,
2019-09-18 09:12:50 +00:00
& zone_id ,
& record_id ,
2019-09-23 08:58:41 +00:00
& email ,
& auth_key ,
2020-03-22 21:10:50 +00:00
) . context ( " Error updating the DNS record " ) ? ;
2019-09-18 09:12:50 +00:00
2019-09-17 15:34:36 +00:00
Ok ( ( ) )
2019-09-17 11:44:12 +00:00
}