Obtaining IP from DNS now works.
authorGreg Burri <greg.burri@gmail.com>
Tue, 13 Jul 2021 14:58:59 +0000 (16:58 +0200)
committerGreg Burri <greg.burri@gmail.com>
Tue, 13 Jul 2021 14:58:59 +0000 (16:58 +0200)
Cargo.lock
src/main.rs

index 2499ac0..70fceb0 100644 (file)
@@ -34,9 +34,9 @@ checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
 
 [[package]]
 name = "cc"
-version = "1.0.68"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
+checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
 
 [[package]]
 name = "cfg-if"
index fa7584e..c5edfc9 100644 (file)
@@ -3,6 +3,10 @@
  * Some similar implementations:
  * - https://github.com/rmarchant/gandi-ddns/blob/master/gandi_ddns.py
  * - https://github.com/brianhp2/gandi-automatic-dns
+ *
+ * TODO:
+ * - Log to stdout with (at least) timestamps.
+ * - Renew function.
  */
 
 #![cfg_attr(debug_assertions, allow(unused_variables, unused_imports, dead_code))]
@@ -12,6 +16,7 @@ use ron::{ de::from_reader, ser::to_writer };
 use serde::{ Deserialize, Serialize };
 use serde_json::Value;
 
+// A generic result of type 'T'.
 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
 
 #[derive(Debug)]
@@ -21,7 +26,7 @@ struct Error {
 
 impl std::fmt::Display for Error {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(&self.message)
+        write!(f, "Error: {}", &self.message)
     }
 }
 
@@ -65,7 +70,9 @@ fn main() -> Result<()> {
     loop {
         let time_beginning_loop = time::Instant::now();
 
-        check_and_update_dns(&config.api_key);
+        if let Err(err) = check_and_update_dns(&config.api_key, &config.domains) {
+            println!("!! Error: {}", err);
+        }
 
         let elapsed = time::Instant::now() - time_beginning_loop;
 
@@ -73,16 +80,25 @@ fn main() -> Result<()> {
             let to_wait = config.delay_between_check - elapsed;
             thread::sleep(to_wait);
         }
-
     }
 }
 
-fn check_and_update_dns(api_key: &str) {
-    /*
-    */
-    //dbg!(get_real_ip());
+fn check_and_update_dns(api_key: &str, domains: &Vec<String>) -> Result<()> {
+    let real_ip = get_real_ip()?;
+    dbg!(&real_ip);
+
+    for domain in domains {
+        let current_ip = get_current_record_ip(api_key, domain)?;
+        dbg!(domain, current_ip);
+
+        if real_ip != current_ip {
+            println!("IP addresses don't match for domain {}: real = {}, dns = {}. Renewing DNS...", domain, real_ip, current_ip);
+            update_record_ip()?;
+            println!("Renewing of {} successfully", domain);
+        }
+    }
 
-    get_current_record_ip(api_key);
+    Ok(())
 }
 
 fn get_real_ip() -> Result<Ipv4Addr> {
@@ -96,16 +112,13 @@ fn get_real_ip() -> Result<Ipv4Addr> {
                 let content = resp.text().unwrap();
                 match content.parse::<IpAddr>() {
                     Ok(IpAddr::V4(ip_v4)) => Ok(ip_v4),
-                    /*Err(_)*/ _ => Err(Box::new(Error { message: String::from("Can't parse IPv4 from ipify") }))
+                    _ => Err(Box::new(Error { message: String::from("Can't parse IPv4 from ipify") }))
                 }
-                //println!("Content:\n{:?}", content);
             } else {
-                //println!("Request unsuccessful:\n{:#?}", resp);
                 Err(Box::new(Error { message: format!("Request unsuccessful: {:#?}", resp) }))
             },
 
         Err(error) => {
-            //println!("Error during request: {:?}", error);
             Err(Box::new(Error { message: format!("Error during request: {:?}", error) }))
         }
     }
@@ -115,7 +128,7 @@ fn request_livedns_gandi(api_key: &str, url_fragment: &str) -> Result<Value> {
     let url = format!("https://api.gandi.net/v5/livedns/{}", url_fragment);
     let client = reqwest::blocking::Client::new();
 
-    match client.get(&url).header("Authorization", format!("Apikey {}", api_key)).send() {
+    match client.get(url).header("Authorization", format!("Apikey {}", api_key)).send() {
         Ok(resp) =>
             if resp.status().is_success() {
                 let content = resp.text().unwrap();
@@ -128,41 +141,18 @@ fn request_livedns_gandi(api_key: &str, url_fragment: &str) -> Result<Value> {
     }
 }
 
-fn get_current_record_ip(api_key: &str) -> Result<Ipv4Addr> {
-
-    request_livedns_gandi(api_key, "domains/euphorik.ch/records/home/A")?; // TODO...
-        // .map()
-        //.map(|json_value| json_value["rrset_values"][0].as_str().unwrap())
-
-    Result::Err(Box::new(Error { message: String::new() }))
-
-
-    //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records";
-    //let url = "https://api.gandi.net/v5/livedns/domains"; // ?sharing_id="
-    //let url = "https://api.gandi.net/v5/organization/organizations";
-    //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records/home/A";
+fn get_current_record_ip(api_key: &str, name: &str) -> Result<Ipv4Addr> {
+    let json_value = request_livedns_gandi(api_key, &format!("domains/euphorik.ch/records/{}/A", name))?;
 
-    /*
-    let client = reqwest::blocking::Client::new();
-
-    match client.get(url).header("Authorization", format!("Apikey {}", api_key)).send() {
-        Ok(resp) =>
-            if resp.status().is_success() {
-                let content = resp.text().unwrap();
-
-                let json: Value = serde_json::from_str(&content).unwrap();
-                let prout = json["rrset_values"][0].as_str().unwrap();
-                println!("IP: {}", prout);
-
-                println!("Content:\n{}", serde_json::to_string_pretty(&json).unwrap());
-            } else {
-                println!("Request unsuccessful:\n{:#?}", resp);
-            },
-        Err(error) =>
-            println!("Error during request: {:?}", error)
+    match &json_value["rrset_values"][0] {
+        Value::String(ip_str) =>
+            Ok(ip_str.parse()?),
+        _ =>
+            Result::Err(Box::new(Error { message: format!("Unable to extract the IP from the JSON answer: {}", json_value) }))
     }
-    */
 }
 
-fn update_record_ip() {
+fn update_record_ip() -> Result<()> {
+    // TODO.
+    Ok(())
 }