2 * API Reference: https://api.gandi.net/docs/livedns/
3 * Some inspiration: https://github.com/rmarchant/gandi-ddns/blob/master/gandi_ddns.py
6 #![cfg_attr(debug_assertions, allow(unused_variables, unused_imports, dead_code))]
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
;
13 type Result
<T
> = std
::result
::Result
<T
, Box
<dyn std
::error
::Error
>>;
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
)
26 impl std
::error
::Error
for Error
{ }
28 #[derive(Debug, Deserialize, Serialize)]
30 delay_between_check
: time
::Duration
,
36 fn default() -> Self {
37 Config
{ delay_between_check
: time
::Duration
::from_secs(60), api_key
: String
::from(""), domains
: Vec
::new() }
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.
45 let file
= File
::create(file_path
)?
;
46 let default_config
= Config
::default();
47 to_writer(file
, &default_config
)?
;
54 const FILE_CONF
: &str = "config.ron";
56 fn main() -> Result
<()> {
57 println!("GANDI DynDNS");
59 let config
= Config
::read(FILE_CONF
)?
;
61 println!("Configuration: {:?}", config
);
64 let time_beginning_loop
= time
::Instant
::now();
66 check_and_update_dns(&config
.api_key
);
68 let elapsed
= time
::Instant
::now() - time_beginning_loop
;
70 if elapsed
< config
.delay_between_check
{
71 let to_wait
= config
.delay_between_check
- elapsed
;
72 thread
::sleep(to_wait
);
78 fn check_and_update_dns(api_key
: &str) {
81 //dbg!(get_real_ip());
83 get_current_record_ip(api_key
);
86 fn get_real_ip() -> Result
<Ipv4Addr
> {
88 let url
= "https://api.ipify.org";
89 let client
= reqwest
::blocking
::Client
::new();
91 match client
.get(url
).send() {
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") }))
99 //println!("Content:\n{:?}", content);
101 //println!("Request unsuccessful:\n{:#?}", resp);
102 Err(Box
::new(Error
{ message
: format!("Request unsuccessful: {:#?}", resp
) }))
106 //println!("Error during request: {:?}", error);
107 Err(Box
::new(Error
{ message
: format!("Error during request: {:?}", error
) }))
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();
116 match client
.get(&url
).header("Authorization", format!("Apikey {}", api_key
)).send() {
118 if resp
.status().is_success() {
119 let content
= resp
.text().unwrap();
120 Ok(serde_json
::from_str(&content
).unwrap())
122 Err(Box
::new(Error
{ message
: format!("Request unsuccessful: {:#?}", resp
) }))
125 Err(Box
::new(Error
{ message
: format!("Error during request: {:?}", error
) }))
129 fn get_current_record_ip(api_key
: &str) -> Result
<Ipv4Addr
> {
131 request_livedns_gandi(api_key
, "domains/euphorik.ch/records/home/A")?
; // TODO...
133 //.map(|json_value| json_value["rrset_values"][0].as_str().unwrap())
135 Result
::Err(Box
::new(Error
{ message
: String
::new() }))
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";
144 let client = reqwest::blocking::Client::new();
146 match client.get(url).header("Authorization", format!("Apikey {}", api_key)).send() {
148 if resp.status().is_success() {
149 let content = resp.text().unwrap();
151 let json: Value = serde_json::from_str(&content).unwrap();
152 let prout = json["rrset_values"][0].as_str().unwrap();
153 println!("IP: {}", prout);
155 println!("Content:\n{}", serde_json::to_string_pretty(&json).unwrap());
157 println!("Request unsuccessful:\n{:#?}", resp);
160 println!("Error during request: {:?}", error)
165 fn update_record_ip() {