2 * API Reference: https://api.gandi.net/docs/livedns/
3 * Some inspiration: https://github.com/rmarchant/gandi-ddns/blob/master/gandi_ddns.py
6 use std
::{fmt
::format
, fs
::File
, net
::{ IpAddr
, Ipv4Addr
}, thread
, time
};
7 use ron
::{ de
::from_reader
, ser
::to_writer
};
8 use serde
::{ Deserialize
, Serialize
};
9 use serde_json
::{ Value
};
11 type Result
<T
> = std
::result
::Result
<T
, Box
<dyn std
::error
::Error
>>;
18 impl std
::fmt
::Display
for Error
{
19 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
20 f
.write_str(&self.message
)
24 impl std
::error
::Error
for Error {}
26 #[derive(Debug, Deserialize, Serialize)]
28 delay_between_check
: time
::Duration
,
33 fn default() -> Self {
34 Config
{ delay_between_check
: time
::Duration
::from_secs(60), api_key
: String
::from("") }
37 fn read(file_path
: &str) -> Result
<Config
> {
38 match File
::open(file_path
) {
39 Ok(file
) => from_reader(file
).map_err(|e
| e
.into()),
40 // The file doesn't exit -> create it with default values.
42 let file
= File
::create(file_path
)?
;
43 let default_config
= Config
::default();
44 to_writer(file
, &default_config
)?
;
51 const FILE_CONF
: &str = "config.ron";
53 fn main() -> Result
<()> {
54 println!("GANDI DynDNS");
56 let config
= Config
::read(FILE_CONF
)?
;
58 println!("Configuration: {:?}", config
);
61 let time_beginning_loop
= time
::Instant
::now();
63 check_and_update_dns(&config
.api_key
);
65 let elapsed
= time
::Instant
::now() - time_beginning_loop
;
67 if elapsed
< config
.delay_between_check
{
68 let to_wait
= config
.delay_between_check
- elapsed
;
69 thread
::sleep(to_wait
);
75 fn check_and_update_dns(api_key
: &str) {
78 //dbg!(get_real_ip());
80 get_current_record_ip(api_key
);
83 fn get_real_ip() -> Result
<Ipv4Addr
> {
85 let url
= "https://api.ipify.org";
86 let client
= reqwest
::blocking
::Client
::new();
88 match client
.get(url
).send() {
90 if resp
.status().is_success() {
91 let content
= resp
.text().unwrap();
92 match content
.parse
::<IpAddr
>() {
93 Ok(IpAddr
::V4(ip_v4
)) => Ok(ip_v4
),
94 /*Err(_)*/ _
=> Err(Box
::new(Error
{ message
: String
::from("Can't parse IPv4 from ipify") }))
96 //println!("Content:\n{:?}", content);
98 //println!("Request unsuccessful:\n{:#?}", resp);
99 Err(Box
::new(Error
{ message
: format!("Request unsuccessful: {:#?}", resp
) }))
103 //println!("Error during request: {:?}", error);
104 Err(Box
::new(Error
{ message
: format!("Error during request: {:?}", error
) }))
109 fn request_livedns_gandi(api_key
: &str, url_fragment
: &str) -> Result
<Value
> {
110 let url
= format!("https://api.gandi.net/v5/livedns/{}", url_fragment
);
111 let client
= reqwest
::blocking
::Client
::new();
113 match client
.get(&url
).header("Authorization", format!("Apikey {}", api_key
)).send() {
115 if resp
.status().is_success() {
116 let content
= resp
.text().unwrap();
117 Ok(serde_json
::from_str(&content
).unwrap())
119 Err(Box
::new(Error
{ message
: format!("Request unsuccessful: {:#?}", resp
) }))
122 Err(Box
::new(Error
{ message
: format!("Error during request: {:?}", error
) }))
126 fn get_current_record_ip(api_key
: &str) -> Result
<Ipv4Addr
> {
128 request_livedns_gandi(api_key
, "domains/euphorik.ch/records/home/A"); // TODO...
130 //.map(|json_value| json_value["rrset_values"][0].as_str().unwrap())
134 //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records";
135 //let url = "https://api.gandi.net/v5/livedns/domains"; // ?sharing_id="
136 //let url = "https://api.gandi.net/v5/organization/organizations";
137 //let url = "https://api.gandi.net/v5/livedns/domains/euphorik.ch/records/home/A";
140 let client = reqwest::blocking::Client::new();
142 match client.get(url).header("Authorization", format!("Apikey {}", api_key)).send() {
144 if resp.status().is_success() {
145 let content = resp.text().unwrap();
147 let json: Value = serde_json::from_str(&content).unwrap();
148 let prout = json["rrset_values"][0].as_str().unwrap();
149 println!("IP: {}", prout);
151 println!("Content:\n{}", serde_json::to_string_pretty(&json).unwrap());
153 println!("Request unsuccessful:\n{:#?}", resp);
156 println!("Error during request: {:?}", error)
161 fn update_record_ip() {