first implementation
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.env
|
||||||
1169
Cargo.lock
generated
Normal file
1169
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,3 +6,8 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
reqwest = { version = "0.11.16", features = ["blocking"] }
|
||||||
|
dotenv_codegen = "0.15.0"
|
||||||
|
serde_json = "1.0.96"
|
||||||
|
regex = "1.7.3"
|
||||||
|
log = "0.4.17"
|
||||||
|
|||||||
11
src/errors.rs
Normal file
11
src/errors.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
pub struct Error {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new(message: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
message: String::from(message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/main.rs
87
src/main.rs
@@ -1,3 +1,88 @@
|
|||||||
|
mod errors;
|
||||||
|
mod public_ip;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate dotenv_codegen;
|
||||||
|
|
||||||
|
use log::{error, info};
|
||||||
|
use public_ip::get_public_ip_address;
|
||||||
|
use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let token = dotenv!("AUTH_BEARER");
|
||||||
|
let zone_id = dotenv!("ZONE_ID");
|
||||||
|
let domain = dotenv!("DOMAIN");
|
||||||
|
|
||||||
|
let (id, content) = get_id_and_content_of_dns(token, zone_id, domain);
|
||||||
|
match get_public_ip_address() {
|
||||||
|
Ok(current_ip_address) => {
|
||||||
|
if content != current_ip_address {
|
||||||
|
update_ip_address(token, zone_id, ¤t_ip_address, domain, &id);
|
||||||
|
} else {
|
||||||
|
info!("Not updating IP Address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => error!("{}", err.message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_id_and_content_of_dns(token: &str, zone_id: &str, domain: &str) -> (String, String) {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
|
let resp = client
|
||||||
|
.get(format!(
|
||||||
|
"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records"
|
||||||
|
))
|
||||||
|
.header(AUTHORIZATION, format!("Bearer {token}",))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let content = resp.text().unwrap();
|
||||||
|
|
||||||
|
let json: Value = serde_json::from_str(&content).unwrap();
|
||||||
|
let array = json["result"].clone();
|
||||||
|
|
||||||
|
let array = array.as_array().unwrap();
|
||||||
|
for value in array {
|
||||||
|
if value["name"] == domain {
|
||||||
|
return (
|
||||||
|
String::from(value["id"].as_str().unwrap()),
|
||||||
|
String::from(value["content"].as_str().unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_ip_address(
|
||||||
|
api_token: &str,
|
||||||
|
zone_id: &str,
|
||||||
|
current_ip_address: &str,
|
||||||
|
domain: &str,
|
||||||
|
dns_id: &str,
|
||||||
|
) {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let resp = client
|
||||||
|
.put(format!(
|
||||||
|
"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{dns_id}"
|
||||||
|
))
|
||||||
|
.header(AUTHORIZATION, format!("Bearer {api_token}",))
|
||||||
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
.body(format!(
|
||||||
|
"{{
|
||||||
|
\"content\": \"{current_ip_address}\",
|
||||||
|
\"name\": \"{domain}\",
|
||||||
|
\"type\": \"A\"
|
||||||
|
}}"
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{}", resp.text().unwrap());
|
||||||
|
|
||||||
|
// if (resp.status()) != 200 {
|
||||||
|
// panic!()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/public_ip.rs
Normal file
38
src/public_ip.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
|
||||||
|
use crate::errors::Error;
|
||||||
|
|
||||||
|
pub fn get_public_ip_address() -> Result<String, Error> {
|
||||||
|
//see https://wiki.ubuntuusers.de/FritzBox/Skripte/
|
||||||
|
|
||||||
|
let resp = Client::new()
|
||||||
|
.post("http://fritz.box:49000/igdupnp/control/WANIPConn1")
|
||||||
|
.header("Content-Type", "text/xml; charset=utf-8")
|
||||||
|
.header(
|
||||||
|
"SoapAction",
|
||||||
|
"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress",
|
||||||
|
)
|
||||||
|
.body("<?xml version='1.0' encoding='utf-8'?> <s:Envelope s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'> <s:Body> <u:GetExternalIPAddress xmlns:u='urn:schemas-upnp-org:service:WANIPConnection:1' /> </s:Body> </s:Envelope>")
|
||||||
|
.send()
|
||||||
|
.map_err(|_| Error::new("Error fetching result via Http"))?;
|
||||||
|
|
||||||
|
if resp.status() != 200 {
|
||||||
|
return Err(Error::new(&format!("Status: {}", resp.status())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let xml = resp.text().map_err(|_| Error::new("No data"))?;
|
||||||
|
|
||||||
|
let regex = Regex::new(r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}")
|
||||||
|
.map_err(|_| Error::new("Regex parsing error"))?;
|
||||||
|
|
||||||
|
let text = regex
|
||||||
|
.captures(&xml)
|
||||||
|
.map(|cap| cap.get(0))
|
||||||
|
.flatten()
|
||||||
|
.map(|cap| cap.as_str())
|
||||||
|
.map(|str| String::from(str))
|
||||||
|
.ok_or(Error::new("Regex no match"));
|
||||||
|
|
||||||
|
text
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user