Add domains in configuration file.
[gandi_dns_update.git] / src / main.rs
1 /*
2 * API Reference: https://api.gandi.net/docs/livedns/
3 * Some inspiration: https://github.com/rmarchant/gandi-ddns/blob/master/gandi_ddns.py
4 */
5
6 #![cfg_attr(debug_assertions, allow(unused_variables, unused_imports, dead_code))]
7
8 use std::{ fmt::format, fs::File, net::{ IpAddr, Ipv4Addr }, thread, time };
9 use ron::{ de::from_reader, ser::to_writer };
10 use serde::{ Deserialize, Serialize };
11 use serde_json::Value;
12
13 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
14
15 #[derive(Debug)]
16 struct Error {
17 message: String
18 }
19
20 impl std::fmt::Display for Error {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.write_str(&self.message)
23 }
24 }
25
26 impl std::error::Error for Error { }
27
28 #[derive(Debug, Deserialize, Serialize)]
29 struct Config {
30 delay_between_check: time::Duration,
31 api_key: String,
32 domains: Vec<String>,
33 }
34
35 impl Config {
36 fn default() -> Self {
37 Config { delay_between_check: time::Duration::from_secs(60), api_key: String::from(""), domains: Vec::new() }
38 }
39
40 fn read(file_path: &str) -> Result<Config> {
41 match File::open(file_path) {
42 Ok(file) => from_reader(file).map_err(|e| e.into()),
43 // The file doesn't exit -> create it with default values.
44 Err(_) => {
45 let file = File::create(file_path)?;
46 let default_config = Config::default();
47 to_writer(file, &default_config)?;
48 Ok(default_config)
49 }
50 }
51 }
52 }
53
54 const FILE_CONF: &str = "config.ron";
55
56 fn main() -> Result<()> {
57 println!("GANDI DynDNS");
58
59 let config = Config::read(FILE_CONF)?;
60
61 println!("Configuration: {:?}", config);
62
63 loop {
64 let time_beginning_loop = time::Instant::now();
65
66 check_and_update_dns(&config.api_key);
67
68 let elapsed = time::Instant::now() - time_beginning_loop;
69
70 if elapsed < config.delay_between_check {
71 let to_wait = config.delay_between_check - elapsed;
72 thread::sleep(to_wait);
73 }
74
75 }
76 }
77
78 fn check_and_update_dns(api_key: &str) {
79 /*
80 */
81 //dbg!(get_real_ip());
82
83 get_current_record_ip(api_key);
84 }
85
86 fn get_real_ip() -> Result<Ipv4Addr> {
87
88 let url = "https://api.ipify.org";
89 let client = reqwest::blocking::Client::new();
90
91 match client.get(url).send() {
92 Ok(resp) =>
93 if resp.status().is_success() {
94 let content = resp.text().unwrap();
95 match content.parse::<IpAddr>() {
96 Ok(IpAddr::V4(ip_v4)) => Ok(ip_v4),
97 /*Err(_)*/ _ => Err(Box::new(Error { message: String::from("Can't parse IPv4 from ipify") }))
98 }
99 //println!("Content:\n{:?}", content);
100 } else {
101 //println!("Request unsuccessful:\n{:#?}", resp);
102 Err(Box::new(Error { message: format!("Request unsuccessful: {:#?}", resp) }))
103 },
104
105 Err(error) => {
106 //println!("Error during request: {:?}", error);
107 Err(Box::new(Error { message: format!("Error during request: {:?}", error) }))
108 }
109 }
110 }
111
112 fn request_livedns_gandi(api_key: &str, url_fragment: &str) -> Result<Value> {
113 let url = format!("https://api.gandi.net/v5/livedns/{}", url_fragment);
114 let client = reqwest::blocking::Client::new();
115
116 match client.get(&url).header("Authorization", format!("Apikey {}", api_key)).send() {
117 Ok(resp) =>
118 if resp.status().is_success() {
119 let content = resp.text().unwrap();
120 Ok(serde_json::from_str(&content).unwrap())
121 } else {
122 Err(Box::new(Error { message: format!("Request unsuccessful: {:#?}", resp) }))
123 },
124 Err(error) =>
125 Err(Box::new(Error { message: format!("Error during request: {:?}", error) }))
126 }
127 }
128
129 fn get_current_record_ip(api_key: &str) -> Result<Ipv4Addr> {
130
131 request_livedns_gandi(api_key, "domains/euphorik.ch/records/home/A")?; // TODO...
132 // .map()
133 //.map(|json_value| json_value["rrset_values"][0].as_str().unwrap())
134
135 Result::Err(Box::new(Error { message: String::new() }))
136
137
138 //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records";
139 //let url = "https://api.gandi.net/v5/livedns/domains"; // ?sharing_id="
140 //let url = "https://api.gandi.net/v5/organization/organizations";
141 //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records/home/A";
142
143 /*
144 let client = reqwest::blocking::Client::new();
145
146 match client.get(url).header("Authorization", format!("Apikey {}", api_key)).send() {
147 Ok(resp) =>
148 if resp.status().is_success() {
149 let content = resp.text().unwrap();
150
151 let json: Value = serde_json::from_str(&content).unwrap();
152 let prout = json["rrset_values"][0].as_str().unwrap();
153 println!("IP: {}", prout);
154
155 println!("Content:\n{}", serde_json::to_string_pretty(&json).unwrap());
156 } else {
157 println!("Request unsuccessful:\n{:#?}", resp);
158 },
159 Err(error) =>
160 println!("Error during request: {:?}", error)
161 }
162 */
163 }
164
165 fn update_record_ip() {
166 }