From f8ad83439cdaeafbc891d5b59959d98e6deab45d Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 15:10:42 +0100 Subject: [PATCH 01/24] Add Clippy to CI --- .forgejo/workflows/build+release.yml | 21 ++++++++++++++++++--- .forgejo/workflows/test.yml | 22 ++++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml index a234073..300b906 100644 --- a/.forgejo/workflows/build+release.yml +++ b/.forgejo/workflows/build+release.yml @@ -9,13 +9,28 @@ on: jobs: test: runs-on: docker + container: forgejo.neshweb.net/ci-docker-images/rust-node:latest steps: + - + name: Add Clippy + run: rustup component add clippy - name: Checking Out Repository Code uses: https://code.forgejo.org/actions/checkout@v3 - - name: Placeholder - run: echo Placeholder Job + name: Set Up Cargo Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - + name: Run Clippy + run: cargo clippy - name: Check if Version in Cargo.toml matches Tag run: | @@ -31,7 +46,7 @@ jobs: needs: test if: success() runs-on: docker - container: rust:latest + container: forgejo.neshweb.net/ci-docker-images/rust-node:latest steps: - name: Installing Node diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 637f42a..12dc226 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -10,7 +10,25 @@ on: jobs: run-tests: runs-on: docker + container: forgejo.neshweb.net/ci-docker-images/rust-node:latest steps: - - name: Placeholder - run: echo Placeholder Job \ No newline at end of file + name: Add Clippy + run: rustup component add clippy + - + name: Checking Out Repository Code + uses: https://code.forgejo.org/actions/checkout@v3 + - + name: Set Up Cargo Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - + name: Run Clippy + run: cargo clippy \ No newline at end of file -- 2.39.2 From d2f89f09380be55c971867c2282ec11ba05a9b85 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 15:11:10 +0100 Subject: [PATCH 02/24] Switch from .json for secrets to .env --- .gitignore | 3 ++- api-key-empty.json | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 api-key-empty.json diff --git a/.gitignore b/.gitignore index 060076d..7b1dd2b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ target/ venv/ .idea/ .vscode/ -api-key.json + +/.env diff --git a/api-key-empty.json b/api-key-empty.json deleted file mode 100644 index 392cfb1..0000000 --- a/api-key-empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "api_key": "" -} \ No newline at end of file -- 2.39.2 From f66fd67159a9a05347bd2ef3e69e88f6377d223f Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 15:12:00 +0100 Subject: [PATCH 03/24] Update readme with planned config setup --- README.md | 94 +++++++++++++++++++------------------------------------ 1 file changed, 32 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index f6bddf8..e28b3b7 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,42 @@ # Cloudflare DNS Updater -## Create and activating a venv -Make sure Python 3.10 or higher is installed. -Use `cd` to change to the location of this repository. -Now, run -``` -py -3.10 -m venv venv -``` -Replace the `-3.10` with other Python versions if necessary (ex.: `-3.11`) +## Using the application -Activate the venv using the command -``` -venv/scripts/activate +The application necessarily requires a valid Cloudflare API Token. +Further the application must be located in the same network as the configured zones. + +| Environment Variable | Required | Usage | +|:--------------------:|:--------:|:----------------------------------:| +| CF_API_TOKEN | x | Cloudflare API Token | +| STATUS_POST_URL | | Post Endpoint for a Uptime Monitor | +*Note: Variables can be stored in a .env file* + +The actual configuration happens in two or more files: +`interfaces.toml` contains all IPv6 interfaces available/used by the zone config files. +`.toml` files in `zone.d` contain settings for individual zones. + +Example: + +*interfaces.toml* +```toml +[[interface]] +alias = "example-interface-1" # this is what a user inputs in the zone.toml +address = ":da5e:d3ff:feeb:4346" # static part of the IP, the rest will be dynamically generated using the host ``` -## Installing the required packages +*zone.d/example.org.toml* +```toml +email = "owner@example.org" # Email of User owning the Zone +name = "example.org" # Zone Name +id = "0183f167a051f1e432c0d931478638b5" # Zone ID -Make sure the venv is activated. - -Run the following command -``` -pip install -r requirements.txt +[[entry]] +name="example.org" # "@" Symbol is not currently supported +type="10" # Options are: "4" (only IPv4/A Record), "6" (only IPv6/AAAA Record) and "10" (both) +interface="example-interface-1" # Only required on type values 6 and 10 ``` -Alternatively, the packages can be installed manually by using `pip install`. - -The full list of packages needed: -- cloudflare (Version 2.0.0 or greater) -- Buildins: - - json - - configparser - - ipaddress - - sys - -## Using the script - -Before running the script, make sure there exists a `config.ini` file next to the `cloudflare_script.py`. -The config has to have the following structure: -```ini -[cloudflare] -TOKEN= - -[server] -HOSTNAME=hostname-of-website.com -``` - -Run the script using the following arguments: -``` -python cloudflare_script.py -``` - -IP-Version can be 4 or 6. -The IP-List has to conform to the following structure and be a json file: -```json -{ - "AAAA": [ - "ipv6 site prefix" - ], - "A": [ - "ipv4 site prefix" - ] -} -``` - -To only get the currently registered DNS records for a given API key, run -``` -python cloudflare_script.py -``` -THis will print all DNS records for the API key with name, ip, cloudflare id and ip-version \ No newline at end of file +## Debian Repository +TODO! \ No newline at end of file -- 2.39.2 From 74b4202e9effe7ad534328922578c97fca983a36 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 15:19:44 +0100 Subject: [PATCH 04/24] Add config files to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7b1dd2b..1feddad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ venv/ .vscode/ /.env +/interfaces.toml +/zones.d \ No newline at end of file -- 2.39.2 From 478dc4dd3ae607f7dabb8d29dc0618fe02abc6cf Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 16:23:44 +0100 Subject: [PATCH 05/24] Adjusted Readme language --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e28b3b7..853e93c 100644 --- a/README.md +++ b/README.md @@ -21,21 +21,23 @@ Example: *interfaces.toml* ```toml +host_address = ":edcb:a098:7654:3210" + [[interface]] -alias = "example-interface-1" # this is what a user inputs in the zone.toml -address = ":da5e:d3ff:feeb:4346" # static part of the IP, the rest will be dynamically generated using the host +name = "example-interface-1" # this is what a user inputs in the zone.toml +address = ":0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host ``` *zone.d/example.org.toml* ```toml email = "owner@example.org" # Email of User owning the Zone -name = "example.org" # Zone Name -id = "0183f167a051f1e432c0d931478638b5" # Zone ID +zone = "example.org" # Zone Name +id = "01234567890abcdefghijklmnopqrstu" # Zone ID [[entry]] -name="example.org" # "@" Symbol is not currently supported -type="10" # Options are: "4" (only IPv4/A Record), "6" (only IPv6/AAAA Record) and "10" (both) -interface="example-interface-1" # Only required on type values 6 and 10 +name = "example.org" # "@" Symbol is not currently supported +rtype = 10 # Options are: "4" (only IPv4/A Record), "6" (only IPv6/AAAA Record) and "10" (both) +interface = "example-interface-1" # Only required on type values 6 and 10 ``` ## Debian Repository -- 2.39.2 From 7efa41a48c29bbdd28880d5f6c0ff8df17790146 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 16:24:18 +0100 Subject: [PATCH 06/24] New config module --- src/config.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +- 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..42d1cd8 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,108 @@ +use std::collections::HashMap; +use std::error::Error; +use std::fs; +use std::hash::Hash; +use log::{error, warn}; +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) struct InterfaceConfig { + pub(crate) host_address: String, + pub(crate) interfaces: HashMap, +} + +impl InterfaceConfig { + pub(crate) fn load() -> Result> { + let cfg: Self = match confy::load(env!("CARGO_PKG_NAME"),"interfaces") { + Ok(data) => data, + Err(e) => { + error!("[ERROR] {}", e); + return Err(Box::new(e)); + } + }; + + Ok(cfg) + } +} + +impl Default for InterfaceConfig { + fn default() -> Self { + InterfaceConfig { + host_address: "::".to_string(), + interfaces: HashMap::from([(" ".to_string(), "::".to_string())]), + } + } +} + +/////////////// + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) struct ZoneEntry { + pub(crate) name: String, + pub(crate) rtype: u8, + pub(crate) interface: String, +} + +impl Default for ZoneEntry { + fn default() -> Self { + ZoneEntry { + name: " ".to_string(), + rtype: 10, + interface: " ".to_string(), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub(crate) struct ZoneConfig { + pub(crate) email: String, + pub(crate) zone: String, + pub(crate) id: String, + #[serde(alias="entry")] + pub(crate) entries: Vec +} + +impl ZoneConfig { + pub(crate) fn load() -> Result, Box> { + let path = confy::get_configuration_file_path(env!("CARGO_PKG_NAME"), "interfaces").expect("Something went wrong with confy"); + let zones_dir = path.parent().expect("Something went wrong with confy").join("zones.d/"); + + let zones = fs::read_dir(zones_dir).unwrap(); + + let mut zone_configs: Vec = vec![]; + + for entry in zones { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + warn!("[WARN] Subdirectory in zones.d detected, this should not be the case"); + } + else { + let zone_config_path = format!("zones.d/{}", path.file_stem() + .expect("stem could not be extracted from filename").to_str() + .expect("&OsStr could not be converted to &str")); + match confy::load(env!("CARGO_PKG_NAME"), zone_config_path.as_str()) { + Ok(data) => zone_configs.push(data), + Err(e) => { + error!("[ERROR] {}", e); + return Err(Box::new(e)); + } + }; + } + } + + Ok(zone_configs) + } +} + +impl Default for ZoneConfig { + fn default() -> Self { + ZoneConfig { + email: " ".to_string(), + zone: " ".to_string(), + id: " ".to_string(), + entries: vec![ZoneEntry::default()], + } + } +} + diff --git a/src/main.rs b/src/main.rs index 1be5b33..5f22021 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use reqwest::blocking::get; use serde_derive::{Deserialize, Serialize}; use std::{fs, thread::{sleep}}; use chrono::{Utc, Duration}; +mod config; mod cloudflare; fn default_key() -> String { @@ -359,7 +360,8 @@ fn main() { let mut now = Utc::now() - Duration::seconds(59); let mut current = now; - loop { + let ifaces = config::InterfaceConfig::load().unwrap(); + let zone_cfgs= config::ZoneConfig::load().unwrap(); now = Utc::now(); if now >= current + Duration::seconds(60) { current = now; -- 2.39.2 From ae80119fa862e0c77f96862f313aa8b676179157 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 16:24:40 +0100 Subject: [PATCH 07/24] (Systemd) logging crates --- Cargo.toml | 3 +++ src/main.rs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index cf13fd4..b7b64ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ serde = "1.0.152" serde_derive = "1.0.152" serde_json = "1.0.93" strum_macros = "0.24.3" +log = "^0.4.20" +systemd-journal-logger = "^2.1.1" +confy = "0.5.1" diff --git a/src/main.rs b/src/main.rs index 5f22021..9cae7e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ use reqwest::blocking::get; use serde_derive::{Deserialize, Serialize}; use std::{fs, thread::{sleep}}; use chrono::{Utc, Duration}; +use log::{info, warn, error, LevelFilter}; +use systemd_journal_logger::JournalLog; mod config; mod cloudflare; @@ -356,6 +358,8 @@ fn update_dns() { } fn main() { + JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error"); + log::set_max_level(LevelFilter::Info); let mut now = Utc::now() - Duration::seconds(59); let mut current = now; -- 2.39.2 From 590093aa5e5738e08a99f075482e756de8a2ba2b Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 18:56:44 +0100 Subject: [PATCH 08/24] Move cloudflare module out of subdirectory. Delete previous code --- src/cloudflare.rs | 1 + src/cloudflare/mod.rs | 269 ------------------------------------------ 2 files changed, 1 insertion(+), 269 deletions(-) create mode 100644 src/cloudflare.rs delete mode 100644 src/cloudflare/mod.rs diff --git a/src/cloudflare.rs b/src/cloudflare.rs new file mode 100644 index 0000000..0d87f3a --- /dev/null +++ b/src/cloudflare.rs @@ -0,0 +1 @@ +const API_BASE: &str = "https://api.cloudflare.com/client/v4"; \ No newline at end of file diff --git a/src/cloudflare/mod.rs b/src/cloudflare/mod.rs deleted file mode 100644 index 96fa37f..0000000 --- a/src/cloudflare/mod.rs +++ /dev/null @@ -1,269 +0,0 @@ -use reqwest::{ - blocking::{Client, Response}, - Url, -}; -use reqwest::{ - header::{HeaderMap, HeaderValue}, - Error, -}; -use serde_derive::{Deserialize, Serialize}; -use std::{collections::HashMap, string::String}; -use strum_macros::{Display, IntoStaticStr}; - -const API_BASE: &str = "https://api.cloudflare.com/client/v4/"; - -#[derive(Serialize, Deserialize)] -struct CloudflareDnsZone { - result: Vec, - success: bool, -} - -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy, Display, IntoStaticStr)] -pub enum CloudflareDnsType { - #[strum(serialize = "A")] - A = 4, - #[strum(serialize = "AAAA")] - AAAA = 6, - CAA, - CERT, - CNAME, - DNSKEY, - DS, - HTTPS, - LOC, - MX, - NAPTR, - NS, - PTR, - SMIMEA, - SRV, - SSHFP, - SVCB, - TLSA, - TXT, - URI -} - -#[derive(Serialize, Deserialize)] -pub struct CloudflareDnsEntry { - id: String, - pub zone_id: String, - pub name: String, - pub r#type: CloudflareDnsType, - content: String, - proxied: bool, -} - -impl CloudflareDnsEntry { - pub fn is_equal(&self, other_entry: &String) -> bool { - return if &self.name == other_entry { - true - } else { - false - }; - } - - pub fn is_ip_new(&self, other_entry: &String) -> bool { - return if &self.content == other_entry { - false - } else { - true - }; - } -} - -pub(crate) struct Instance { - api_key: String, - pub dns_entries: Vec, - dns_email: String, -} - -impl Instance { - pub fn new(key: &String) -> Instance { - Instance { - api_key: key.clone(), - dns_entries: Vec::new(), - dns_email: "".to_string(), - } - } - - /// Loads all DNS entires in a Zone as a [`CloudflareDnsEntry`] Vector - /// - /// * `email` : E-Mail Address associated with the Zone, required for [`generate_headers()`] - /// * `zone_id` : Zone to be used for the Request - /// * `return` : Returns true if a recoverable Error was encountered, true otherwise - pub fn load_entries(&mut self, email: &String, zone_id: &String) -> Result<(), Error> { - self.dns_email = email.clone(); - let endpoint = format!("{}zones/{}/dns_records", API_BASE, zone_id); - - match self.get(&endpoint) { - Ok(response) => { - if response.status().is_success() { - let entries = match response.json::() { - Ok(data) => data, - Err(e) => { - panic!("{:#?}", e) - } - }; - - for entry in entries.result { - self.dns_entries.append(&mut vec![entry]); - } - - return Ok(()); - } - else { - println!("Server returned Status Code: {}", response.status()); - return Err(response.error_for_status().err().unwrap()); - } - } - Err(e) => return Err(e), - } - } - - /// Shorthand for Cloudflare API Requests - /// - /// Automatically appends Auth Headers and parses the URL - /// - /// * `url` : URL for the request, will be parsed to [`Url`] - fn get(&self, url: &str) -> Result { - let url_parsed = match Url::parse(url) { - Ok(url) => url, - Err(e) => panic!("{:#?}", e), // If this call fails the API changed - }; // Continuing without a code update is not possible - - let result = Client::new() - .get(url_parsed) - .headers(self.generate_headers()) - .send(); - - return result; - } - - /// Shorthand for Cloudflare API Requests - /// - /// Automatically appends Auth Headers and parses the URL - /// - /// * `url` : URL for the request, will be parsed to [`Url`] - /// * `data` : PUT request body - fn put(&self, url: &str, data: &HashMap<&str, &str>) -> Result { - let url_parsed = match Url::parse(url) { - Ok(url) => url, - Err(e) => panic!("{:#?}", e), // If this call fails the API changed - }; // Continuing without a code update is not possible - - let result = match Client::new() - .put(url_parsed) - .headers(self.generate_headers()) - .json(data) - .send() - { - Ok(res) => Ok(res), - Err(e) => Err(e), - }; - - return result; - } - - /// Shorthand for Cloudflare API Requests - /// - /// Automatically appends Auth Headers and parses the URL - /// - /// * `url` : URL for the request, will be parsed to [`Url`] - /// * `data` : POST request body - fn post(&self, url: &str, data: &HashMap<&str, &str>) -> Result { - let url_parsed = match Url::parse(url) { - Ok(url) => url, - Err(e) => panic!("{:#?}", e), // If this call fails the API changed - }; // Continuing without a code update is not possible - - let result = match Client::new() - .post(url_parsed) - .headers(self.generate_headers()) - .json(data) - .send() - { - Ok(res) => Ok(res), - Err(e) => Err(e), - }; - - return result; - } - - /// Updates the given DNS entry with the given IP - /// - /// * `entry` : DNS entry to update - /// * `new_ip` : IP used to Update the DNS entry, can be IPv4 or IPv6 - /// * `return1` : Returns success of the API call, extracted from the HTTP Response body - pub fn update_entry(&self, entry: &CloudflareDnsEntry, new_ip: &String) -> Result { - let endpoint = format!( - "{}zones/{}/dns_records/{}", - API_BASE, entry.zone_id, entry.id - ); - - let mut json_body = HashMap::new(); - json_body.insert("type", entry.r#type.clone().into()); - json_body.insert("name", entry.name.as_ref()); - json_body.insert("content", new_ip); - json_body.insert("ttl", "1"); - - match self.put(&endpoint, &json_body) { - Ok(response) => { - let data = response - .json::() - .expect("Response should always be valid JSON"); - - let success = data["success"] - .as_bool() - .expect("JSON should always include success bool"); - return Ok(success); - } - Err(e) => return Err(e), - } - } - - pub fn create_entry( - &self, - zone_id: &String, - r#type: &str, - name: &str, - ip: &String, - ) -> Result { - let endpoint = format!("{}zones/{}/dns_records", API_BASE, zone_id); - - let mut json_body = HashMap::new(); - json_body.insert("type", r#type); - json_body.insert("name", name); - json_body.insert("content", ip); - json_body.insert("ttl", "1"); - - match self.post(&endpoint, &json_body) { - Ok(response) => { - let data = response - .json::() - .expect("Response should always be valid JSON"); - - let success = data["success"] - .as_bool() - .expect("JSON should always include success bool"); - - return Ok(success); - } - Err(e) => return Err(e), - } - } - - /// Generate Cloudflare API v4 Auth Headers - fn generate_headers(&self) -> HeaderMap { - let mut headers = HeaderMap::new(); - headers.insert( - "X-Auth-Key", - HeaderValue::from_str(self.api_key.as_ref()).unwrap(), - ); - headers.insert( - "X-Auth-Email", - HeaderValue::from_str(self.dns_email.as_ref()).unwrap(), - ); - headers - } -} -- 2.39.2 From 0b8b2299da372ce9823f121c0c1aa269ffc0f7bc Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 25 Dec 2023 18:58:17 +0100 Subject: [PATCH 09/24] Change Cargo.toml Version requirements + add confy --- Cargo.lock | 841 ++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 15 +- 2 files changed, 516 insertions(+), 340 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c46c92f..8501eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -18,10 +39,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "base64" -version = "0.21.0" +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bitflags" @@ -30,22 +66,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bumpalo" -version = "3.12.0" +name = "bitflags" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -55,17 +100,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -73,28 +117,34 @@ name = "cloudflare-dns-updater" version = "0.2.8" dependencies = [ "chrono", + "confy", + "dotenv", + "log", "reqwest", "serde", "serde_derive", "serde_json", "strum_macros", + "systemd-journal-logger", ] [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "confy" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" dependencies = [ - "termcolor", - "unicode-width", + "directories", + "serde", + "thiserror", + "toml", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -102,72 +152,67 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] -name = "cxx" -version = "1.0.91" +name = "directories" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "dirs-sys", ] [[package]] -name = "cxx-build" -version = "1.0.91" +name = "dirs-sys" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", + "libc", + "redox_users", + "winapi", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.91" +name = "dotenv" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] -name = "fastrand" -version = "1.9.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "instant", + "libc", + "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fnv" version = "1.0.7" @@ -191,51 +236,51 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -247,10 +292,27 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.15" +name = "getrandom" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -267,9 +329,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -279,18 +341,15 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -299,9 +358,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -316,15 +375,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -359,33 +418,32 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -393,40 +451,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -439,50 +488,66 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "cc", + "bitflags 2.4.1", + "libc", + "redox_syscall", ] [[package]] -name = "log" -version = "0.4.17" +name = "linux-raw-sys" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ - "cfg-if", + "value-bag", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "wasi", + "windows-sys 0.48.0", ] [[package]] @@ -503,48 +568,47 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "once_cell" -version = "1.17.1" +name = "object" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -555,13 +619,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.43", ] [[package]] @@ -572,11 +636,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -585,15 +648,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -603,51 +666,53 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "redox_users" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "winapi", + "getrandom", + "libredox", + "thiserror", ] [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64", "bytes", @@ -670,6 +735,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -681,39 +747,52 @@ dependencies = [ ] [[package]] -name = "rustversion" -version = "1.0.11" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "ryu" -version = "1.0.12" +name = "rustix" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "windows-sys 0.42.0", + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] -name = "scratch" -version = "1.0.3" +name = "rustversion" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -722,9 +801,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -732,26 +811,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.43", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -772,21 +854,21 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.4.7" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -799,7 +881,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.109", ] [[package]] @@ -814,37 +896,78 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.3.0" +name = "syn" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "systemd-journal-logger" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f3848dd723f2a54ac1d96da793b32923b52de8dfcced8722516dac312a5b2a" +dependencies = [ + "log", + "rustix", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.48.0", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ - "winapi-util", + "thiserror-impl", ] [[package]] -name = "time" -version = "0.1.45" +name = "thiserror-impl" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "proc-macro2", + "quote", + "syn 2.0.43", ] [[package]] @@ -864,19 +987,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", "socket2", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -891,9 +1013,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -903,6 +1025,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -911,41 +1042,40 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -956,23 +1086,23 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "url" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "value-bag" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" + [[package]] name = "vcpkg" version = "0.2.15" @@ -981,20 +1111,13 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1003,9 +1126,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1013,24 +1136,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.43", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -1040,9 +1163,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1050,28 +1173,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1093,15 +1216,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1109,91 +1223,152 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index b7b64ec..3c5b0ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "0.4.23" -reqwest = { version = "0.11.14", features = ["blocking", "json"] } -serde = "1.0.152" -serde_derive = "1.0.152" -serde_json = "1.0.93" -strum_macros = "0.24.3" +chrono = "^0.4.23" +reqwest = { version = "^0.11.14", features = ["blocking", "json"] } +serde = "^1.0.152" +serde_derive = "^1.0.152" +serde_json = "^1.0.93" +strum_macros = "^0.24.3" log = "^0.4.20" systemd-journal-logger = "^2.1.1" -confy = "0.5.1" +confy = "^0.5.1" +dotenv = "^0.15.0" -- 2.39.2 From 1ae5d2b49fc8725e82e2f10ee1c254382afd4607 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:05:35 +0100 Subject: [PATCH 10/24] Added dependencies for improved API handling --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 8501eb9..f53a033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,6 +119,7 @@ dependencies = [ "chrono", "confy", "dotenv", + "ipnet", "log", "reqwest", "serde", @@ -126,6 +127,7 @@ dependencies = [ "serde_json", "strum_macros", "systemd-journal-logger", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3c5b0ce..c6d37d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ log = "^0.4.20" systemd-journal-logger = "^2.1.1" confy = "^0.5.1" dotenv = "^0.15.0" +ipnet = "^2.9.0" +url = "2.5.0" -- 2.39.2 From 445b22fecdd661dea7f9466f93a7324312720dcf Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:06:38 +0100 Subject: [PATCH 11/24] Conditional logging destination --- src/config.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/config.rs b/src/config.rs index 42d1cd8..8734b2d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,7 +16,10 @@ impl InterfaceConfig { let cfg: Self = match confy::load(env!("CARGO_PKG_NAME"),"interfaces") { Ok(data) => data, Err(e) => { - error!("[ERROR] {}", e); + match connected_to_journal() { + true => error!("[ERROR] {e}"), + false => eprintln!("[ERROR] {e}") + } return Err(Box::new(e)); } }; @@ -75,7 +78,11 @@ impl ZoneConfig { let entry = entry?; let path = entry.path(); if path.is_dir() { - warn!("[WARN] Subdirectory in zones.d detected, this should not be the case"); + let warn_msg = "Subdirectory in zones.d detected, this should not be the case"; + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => eprintln!("[WARN] {warn_msg}"), + } } else { let zone_config_path = format!("zones.d/{}", path.file_stem() @@ -84,7 +91,10 @@ impl ZoneConfig { match confy::load(env!("CARGO_PKG_NAME"), zone_config_path.as_str()) { Ok(data) => zone_configs.push(data), Err(e) => { - error!("[ERROR] {}", e); + match connected_to_journal() { + true => error!("[ERROR] {e}"), + false => eprintln!("[ERROR] {e}"), + } return Err(Box::new(e)); } }; -- 2.39.2 From fbf31a4115878e7e6a1a0d538a5fa28e5efb25c0 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:09:18 +0100 Subject: [PATCH 12/24] Adjusted Readme to reflect code changes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 853e93c..74b14b2 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ Example: *interfaces.toml* ```toml -host_address = ":edcb:a098:7654:3210" +host_address = "::edcb:a098:7654:3210" [[interface]] name = "example-interface-1" # this is what a user inputs in the zone.toml -address = ":0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host +address = "::0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host ``` *zone.d/example.org.toml* @@ -36,7 +36,7 @@ id = "01234567890abcdefghijklmnopqrstu" # Zone ID [[entry]] name = "example.org" # "@" Symbol is not currently supported -rtype = 10 # Options are: "4" (only IPv4/A Record), "6" (only IPv6/AAAA Record) and "10" (both) +type = ["AAAA", "A"] # Options are: "A" (IPv4/A Record) and/or "AAAA" (IPv6/AAAA Record) interface = "example-interface-1" # Only required on type values 6 and 10 ``` -- 2.39.2 From ab235972183d1c4dd48cc272609aaa70fa97935e Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:10:00 +0100 Subject: [PATCH 13/24] Rewrite Cloudflare module, minor changes to config module --- src/cloudflare.rs | 358 +++++++++++++++++++++++++++++++++++++++++++++- src/config.rs | 39 +++-- 2 files changed, 388 insertions(+), 9 deletions(-) diff --git a/src/cloudflare.rs b/src/cloudflare.rs index 0d87f3a..33ac704 100644 --- a/src/cloudflare.rs +++ b/src/cloudflare.rs @@ -1 +1,357 @@ -const API_BASE: &str = "https://api.cloudflare.com/client/v4"; \ No newline at end of file +use std::collections::HashMap; +use std::env; +use std::env::VarError; +use std::error::Error; +use std::net::{Ipv4Addr, Ipv6Addr}; +use log::{error, warn}; +use reqwest::header::{HeaderMap, HeaderValue}; +use reqwest::{Url}; +use reqwest::blocking::{Response, Client}; +use serde_derive::{Deserialize, Serialize}; +use strum_macros::{Display, IntoStaticStr}; +use systemd_journal_logger::connected_to_journal; +use url::ParseError; +use crate::config::{ZoneConfig, ZoneEntry}; + +const API_BASE: &str = "https://api.cloudflare.com/client/v4"; + +#[derive(Serialize, Deserialize, Debug)] +struct CloudflareApiResults { + result: Vec, + success: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +struct CloudflareApiResult { + success: bool, +} + +pub(crate) struct CloudflareZone { + email: String, + key: String, + id: String, +} + +impl CloudflareZone { + pub(crate) fn new(zone: &ZoneConfig) -> Result { + let key = env::var("CF_API_TOKEN")?; + Ok(Self { + email: zone.email.clone(), + key, + id: zone.id.clone(), + }) + } + + fn generate_auth_headers(&self) -> HeaderMap { + let mut headers = HeaderMap::new(); + headers.insert( + "X-Auth-Email", + HeaderValue::from_str(self.email.as_str()).expect("After CloudflareZone gets created the required Values should exist"), + ); + headers.insert( + "X-Auth-Key", + HeaderValue::from_str(self.key.as_str()).expect("After CloudflareZone gets created the required Values should exist"), + ); + headers + } + + pub(crate) fn get_entries(&self) -> Result, ()> { + let endpoint = format!("{}/zones/{}/dns_records", API_BASE, self.id); + + match self.get(&endpoint) { + Ok(response) => { + return if response.status().is_success() { + let entries = match response.json::() { + Ok(data) => data, + Err(e) => { + let err_msg = format!("Unable to parse API response. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + return Err(()) + } + }; + + Ok(entries.result) + } else { + let err_msg = format!("Unable to fetch Cloudflare Zone Entries. Error: {}", response.status()); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } + Err(e) => { + let err_msg = format!("Unable to access Cloudflare API. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + }, + } + } + + pub(crate) fn update(&self, entry: &ZoneEntry, r#type: &DnsRecordType, id: &String, ipv6: Option, ipv4: Option) -> Result<(), ()> { + let endpoint = format!("{}/zones/{}/dns_records/{}", API_BASE, self.id, id); + + match r#type { + DnsRecordType::A => { + return if let Some(ip) = ipv4 { + let json_body = self.create_body("A", &entry.name, ip.to_string().as_str()); + + match self.put(&endpoint, &json_body) { + Ok(response) => { + self.validate_response(response) + }, + Err(e) => { + let err_msg = format!("Unable to access Cloudflare API. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } + } else { + let err_msg = "Missing IPv4 for Update."; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + }, + DnsRecordType::AAAA => { + return if let Some(ip) = ipv6 { + let json_body = self.create_body("AAAA", &entry.name, ip.to_string().as_str()); + + match self.put(&endpoint, &json_body) { + Ok(response) => { + self.validate_response(response) + }, + Err(e) => { + let err_msg = format!("Unable to access Cloudflare API. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } + } else { + let err_msg = "Missing IPv6 for Update."; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + }, + _ => { + let warn_msg = "Config contains unsupported type identifier"; + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + return Err(()) + } + } + } + + pub(crate) fn create(&self, entry: &ZoneEntry, r#type: &DnsRecordType, ipv6: Option, ipv4: Option) -> Result<(), ()> { + let endpoint = format!("{}/zones/{}/dns_records", API_BASE, self.id); + + match r#type { + DnsRecordType::A => { + return if let Some(ip) = ipv4 { + let json_body = self.create_body("A", &entry.name, ip.to_string().as_str()); + + match self.post(&endpoint, &json_body) { + Ok(response) => { + self.validate_response(response) + }, + Err(e) => { + let err_msg = format!("Unable to access Cloudflare API. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } + } else { + let err_msg = "Missing IPv4 for Update."; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + }, + DnsRecordType::AAAA => { + return if let Some(ip) = ipv6 { + let json_body = self.create_body("AAAA", &entry.name, ip.to_string().as_str()); + + match self.post(&endpoint, &json_body) { + Ok(response) => { + self.validate_response(response) + }, + Err(e) => { + let err_msg = format!("Unable to access Cloudflare API. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } + } else { + let err_msg = "Missing IPv6 for Update."; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + }, + _ => { + let warn_msg = "Config contains unsupported type identifier"; + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + return Err(()) + } + } + } + + fn get(&self, url: &str) -> Result> { + let url_parsed = self.parse_url(url)?; + + match Client::new() + .get(url_parsed) + .headers(self.generate_auth_headers()) + .send() { + Ok(result) => Ok(result), + Err(e) => Err(Box::new(e)), + } + } + + fn post(&self, url: &str, data: &HashMap) -> Result> { + let url_parsed = self.parse_url(url)?; + + match Client::new() + .post(url_parsed) + .headers(self.generate_auth_headers()) + .json(data) + .send() { + Ok(result) => Ok(result), + Err(e) => Err(Box::new(e)), + } + } + + fn put(&self, url: &str, data: &HashMap) -> Result> { + let url_parsed = self.parse_url(url)?; + + match Client::new() + .put(url_parsed) + .headers(self.generate_auth_headers()) + .json(data) + .send() { + Ok(result) => Ok(result), + Err(e) => Err(Box::new(e)), + } + } + + fn parse_url(&self, input: &str) -> Result { + match Url::parse(input) { + Ok(url) => Ok(url), + Err(e) => { + let err_msg = format!("Unable to parse URL. Error: {}", e); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + return Err(e) + } + } + } + + fn create_body(&self, r#type: &str, name: &String, ip: &str) -> HashMap { + let mut body = HashMap::new(); + body.insert("type".to_string(), r#type.to_string()); + body.insert("name".to_string(), name.clone()); + body.insert("content".to_string(), ip.to_string()); + body + } + + fn validate_response(&self, response: Response) -> Result<(), ()> { + if response.status().is_success() { + let data = match response.json::() { + Ok(data) => data, + Err(e) => { + let err_msg = format!("Unable to parse API response. Error: {e}"); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + return Err(()) + } + }; + + match data.success { + true => return Ok(()), + false => { + let err_msg = format!("Unexpected error while updating DNS record. Info: {:?}", data); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + return Err(()) + } + } + } else { + let err_msg = format!("Unable to post/put Cloudflare DNS entry. Error: {}", response.status()); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + Err(()) + } + } +} + +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy, Display, IntoStaticStr)] +pub(crate) enum DnsRecordType { + A, + AAAA, + CAA, + CERT, + CNAME, + DNSKEY, + DS, + HTTPS, + LOC, + MX, + NAPTR, + NS, + PTR, + SMIMEA, + SRV, + SSHFP, + SVCB, + TLSA, + TXT, + URI +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub(crate) struct CloudflareDnsRecord { + pub(crate) id: String, + pub(crate) name: String, + pub(crate) r#type: DnsRecordType, + content: String +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 8734b2d..2858c8a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,13 +2,19 @@ use std::collections::HashMap; use std::error::Error; use std::fs; use std::hash::Hash; +use std::net::Ipv6Addr; +use ipnet::{IpAdd, IpBitAnd, IpBitOr, IpSub}; +use std::str::FromStr; use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; +use systemd_journal_logger::connected_to_journal; +use crate::cloudflare::DnsRecordType; +use crate::cloudflare::DnsRecordType::{A, AAAA}; #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct InterfaceConfig { - pub(crate) host_address: String, - pub(crate) interfaces: HashMap, + pub(crate) host_address: Ipv6Addr, + pub(crate) interfaces: HashMap, } impl InterfaceConfig { @@ -26,13 +32,30 @@ impl InterfaceConfig { Ok(cfg) } + + pub(crate) fn full_v6(&self, interface_name: &String, host_v6: Ipv6Addr) -> Result { + let host_range = Ipv6Addr::from(host_v6.saturating_sub(self.host_address)); + let interface_address = match self.interfaces.get(interface_name) { + Some(address) => address.clone(), + None => { + let err_msg = "Malformed IP in interfaces.toml"; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + return Err(()); + } + }; + let interface_ip = host_range.bitor(interface_address); + Ok(interface_ip) + } } impl Default for InterfaceConfig { fn default() -> Self { InterfaceConfig { - host_address: "::".to_string(), - interfaces: HashMap::from([(" ".to_string(), "::".to_string())]), + host_address: Ipv6Addr::from_str("::").expect("Malformed literal in code"), + interfaces: HashMap::from([(" ".to_string(), Ipv6Addr::from(0))]), } } } @@ -42,7 +65,7 @@ impl Default for InterfaceConfig { #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct ZoneEntry { pub(crate) name: String, - pub(crate) rtype: u8, + pub(crate) r#type: Vec, pub(crate) interface: String, } @@ -50,7 +73,7 @@ impl Default for ZoneEntry { fn default() -> Self { ZoneEntry { name: " ".to_string(), - rtype: 10, + r#type: vec![A, AAAA], interface: " ".to_string(), } } @@ -59,7 +82,7 @@ impl Default for ZoneEntry { #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct ZoneConfig { pub(crate) email: String, - pub(crate) zone: String, + pub(crate) name: String, pub(crate) id: String, #[serde(alias="entry")] pub(crate) entries: Vec @@ -109,7 +132,7 @@ impl Default for ZoneConfig { fn default() -> Self { ZoneConfig { email: " ".to_string(), - zone: " ".to_string(), + name: " ".to_string(), id: " ".to_string(), entries: vec![ZoneEntry::default()], } -- 2.39.2 From 31892b27fcbe3c7e8035d31527ead49b8c77e002 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:10:10 +0100 Subject: [PATCH 14/24] Implement rewrite in main --- src/main.rs | 632 +++++++++++++++++++++++----------------------------- 1 file changed, 282 insertions(+), 350 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9cae7e1..fcead42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,381 +1,313 @@ -use cloudflare::{Instance, CloudflareDnsType}; +/*use cloudflare_old::{Instance, CloudflareDnsType};*/ use reqwest::blocking::get; use serde_derive::{Deserialize, Serialize}; -use std::{fs, thread::{sleep}}; +use std::{thread::{sleep}}; +use std::error::Error; +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; use chrono::{Utc, Duration}; +use dotenv::dotenv; use log::{info, warn, error, LevelFilter}; -use systemd_journal_logger::JournalLog; +use reqwest::StatusCode; +use systemd_journal_logger::{connected_to_journal, JournalLog}; +use crate::cloudflare::{CloudflareZone, DnsRecordType}; + mod config; mod cloudflare; -fn default_key() -> String { - return "".to_string(); +struct Addresses { + ipv4_uri: String, + ipv6_uri: String, + ipv4: Ipv4Addr, + ipv6: Ipv6Addr, } -#[derive(Clone)] -struct ChangeTracker { - created: u16, - updated: u16, - unchanged: u16, - error: u16 -} - -impl ChangeTracker { - fn new() -> ChangeTracker { - ChangeTracker { - created: 0, - updated: 0, - unchanged: 0, - error: 0 - } - } - - fn print(&self, f: &str) { - match f { - "" => { - self.print("simple"); - } - "simple" => { - println!("{} created", self.created); - println!("{} updated", self.updated); - println!("{} unchanged", self.unchanged); - println!("{} errors", self.error); - } - &_ => { - println!("unknown format pattern '{}', defaulting to 'simple'", f); - self.print("simple"); - } - } - } -} - -#[derive(Serialize, Deserialize)] -struct DnsEntry { - name: String, - type4: bool, - type6: bool, - interface: Option, -} - -impl DnsEntry { - /// Updates a single DNS Entry - /// - /// * `change4` : IPv4 DNS change tracker, used to print details about the results of the update run after it concludes - /// * `change6` : Same as `change4` but for IPv6 - /// * `ips` : Enum of current IPv4 and IPv6 base, used to check against and update the DNS record - /// * `agent` : Cloudflare Auth Agent, provides the needed Authentication headers for the API Calls - /// * `zone_id` : Cloudfalre ID of the Zone this DNS entry resides in, needed for the API Calls - /// - /// * `return` : Returns true if the DNS Update failed due to an Error, otherwise returns false - fn update( - &self, - change4: &mut ChangeTracker, - change6: &mut ChangeTracker, - ips: &Ips, - agent: &Instance, - zone_id: &String - ) -> bool { - let mut found4 = false; - let mut found6 = false; - - // Loops over every Entry in the List fetched from the Cloudflare API - // If the entry is in this List we need to update it, if not it needs to be created - for cloudflare_entry in &agent.dns_entries { - if cloudflare_entry.is_equal(&self.name) { - // Update IPv4 Entry - if cloudflare_entry.r#type == CloudflareDnsType::A && self.type4 { - found4 = true; - - if cloudflare_entry.is_ip_new(&ips.ipv4) { - match agent.update_entry(cloudflare_entry, &ips.ipv4) { - Ok(success) => { - if success { - change4.updated += 1; - } - else { - change4.error += 1; - } - }, - Err(_e) => { return true } // if an error is detected any subsequent calls are likely to fail as well - }; - } - else { - change4.unchanged += 1; - } - } - // Update IPv6 Entry - else if cloudflare_entry.r#type == CloudflareDnsType::AAAA && self.type6 { - found6 = true; - let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); - - if cloudflare_entry.is_ip_new(&ipv6) { - match agent.update_entry(cloudflare_entry, &ipv6) { - Ok(success) => { - if success { - change6.updated += 1 - } - else { - change6.error += 1 - } - }, - Err(_e) => { return true } // if an error is detected any subsequent calls are likely to fail as well - }; - } - else { - change6.unchanged += 1; - } - } - // ignore otherwise - else {} - } - } - - if !found4 && self.type4 { - match agent.create_entry(zone_id, "A", &self.name, &ips.ipv4) { - Ok(success) => { - if success { - change4.created += 1; - } - else { - change4.error += 1; - }; - }, - Err(_e) => { return true } // early return since if an error is detected any subsequent calls are likely to fail as well - } - } - - if !found6 && self.type6 { - let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); - match agent.create_entry(zone_id, "AAAA", &self.name, &ipv6) { - Ok(success) => { - if success { - change6.created += 1; - } - else { - change6.error += 1; - }; - }, - Err(_e) => { return true } // if an error is detected any subsequent calls are likely to fail as well - }; - } - - return false; // If this point is reached no error has been caught therefore returning false - } -} - -#[derive(Serialize, Deserialize)] -struct DnsZone { - email: String, - name: String, - id: String, - dns_entries: Vec, -} - -impl DnsZone { - /// Attempts to Update the DNS Entries of the Zone with a new IP - /// - /// * `change4` : IPv4 DNS change tracker, used to print details about the results of the update run after it concludes - /// * `change6` : Same as `change4` but for IPv6 - /// * `ips` : Enum of current IPv4 and IPv6 base, used to check against and update the DNS record - /// * `key` : API Key needed to create the Auth Agent for the API - /// - /// * `return` : Returns true if the DNS Updates failed due to an Error, false otherwise - fn update( - &self, - change4: &mut ChangeTracker, - change6: &mut ChangeTracker, - ips: &Ips, - key: &String - ) -> bool { - let mut agent = Instance::new(key); - - match agent.load_entries(&self.email, &self.id) { - Ok(_) => { - let mut results = Vec::::new(); - - for entry in &self.dns_entries { - let result = entry.update(change4, change6, &ips, &agent, &self.id); - results.append(&mut vec!(result)); - if result { - break; // if one update fails all others are likely to fail as well - } - } - - let result = results.contains(&true); - - return result; - }, - Err(_e) => { return true }, // Early return since an Error likely means subsequent calls will also fail - } - } -} - -#[derive(Serialize, Deserialize)] -struct DnsConfig { - ipv6_interface: String, - #[serde(default = "default_key")] - api_key: String, - zones: Vec, -} - -impl DnsConfig { - fn new() -> DnsConfig { - DnsConfig { - ipv6_interface: "".to_string(), - api_key: "".to_string(), - zones: Vec::new(), - } - } - - fn load(&mut self) { - let file_contents = match fs::read_to_string("config.json") { - Ok(data) => data, - Err(e) => panic!("{:#?}", e), - }; - let config_parse: DnsConfig = match serde_json::from_str(&file_contents) { - Ok(data) => data, - Err(e) => panic!("{:#?}", e), +impl Addresses { + fn new() -> Result> { + let mut ret = Self { + ipv4_uri: "https://am.i.mullvad.net/ip".to_string(), + ipv6_uri: "https://ipv6.am.i.mullvad.net/ip".to_string(), + ipv4: Ipv4Addr::new(0, 0, 0, 0), + ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0) }; - let key_file_contents = match fs::read_to_string("api-key.json") { - Ok(data) => data, - Err(e) => panic!("{:#?}", e), - }; - let api_key_parse: serde_json::Value = match serde_json::from_str(&key_file_contents) { - Ok(data) => data, - Err(e) => panic!("{:#?}", e), - }; - - *self = config_parse; - self.api_key = api_key_parse["api_key"].as_str().unwrap().to_owned(); - } -} - -struct Ips { - ipv4: String, - ipv6base: String, -} - -impl Ips { - fn new() -> Ips { - Ips { - ipv4: "0.0.0.0".to_string(), - ipv6base: ":".to_string(), - } - } - - /// Get the current IPv4 address and IPv6 prefix from Mullvad API - /// - /// Returns true if the get succeeded and false if it failed. - /// The IPv6 Prefix is generated by removing the IPv6 Interface from the fetched IPv6 address - fn get(&mut self, ipv6_interface: &str) -> bool { - let ipv4uri = "https://am.i.mullvad.net/ip"; - let ipv6uri = "https://ipv6.am.i.mullvad.net/ip"; - let mut ret; - - match get(ipv4uri) { - Ok(data) => { - if data.status() == 200 { - self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(); - ret = true; + match ret.get_v4() { + Ok(ip) => ret.ipv4 = ip, + Err(_) => { + let err_msg = format!("Unable to fetch IPv4 from '{}' during init. Aborting!", ret.ipv4_uri); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), } - else { - ret = false; + panic!("{}", err_msg); + } + } + + match ret.get_v6() { + Ok(ip) => ret.ipv6 = ip, + Err(_) => { + let err_msg = format!("Unable to fetch IPv6 from '{}' during init. Aborting!", ret.ipv6_uri); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + panic!("{}", err_msg); + } + } + + Ok(ret) + } + + fn check_new(&mut self, update: bool) -> bool { + let mut new = false; + + match self.get_v4() { + Ok(ip) => { + if ip != self.ipv4 { + let info_msg = format!("IPv4 changed from '{}' to '{}'", self.ipv4, ip); + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + new = true; + if update { self.ipv4 = ip } + } + } + Err(e) => { + let warn_msg = format!("Unable to fetch IPv4 from '{}'. Error: {}", self.ipv4_uri, e); + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + } + } + + match self.get_v6() { + Ok(ip) => { + if ip != self.ipv6 { + let info_msg = format!("IPv6 changed from '{}' to '{}'", self.ipv6, ip); + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + new = true; + if update { self.ipv6 = ip } + } + } + Err(e) => { + let warn_msg = format!("Unable to fetch IPv6 from '{}'. Error: {}", self.ipv6_uri, e); + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + } + } + + new + } + + fn get_v4(&self) -> Result { + match get(&self.ipv4_uri) { + Ok(res) => { + match res.status() { + StatusCode::OK => { + let ip_string = res.text().expect("Returned data should always contain text").trim_end().to_owned(); + Ok(Ipv4Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable")) + }, + _ => { + let warn_msg = format!("Unexpected HTTP status {}", res.status()); + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + Ok(Ipv4Addr::new(0, 0, 0, 0)) + } + } + } + Err(e) => Err(e), + } + + } + + fn get_v6(&self) -> Result { + match get(&self.ipv6_uri) { + Ok(res) => { + match res.status() { + StatusCode::OK => { + let ip_string = res.text().expect("Returned data should always contain text").trim_end().to_owned(); + Ok(Ipv6Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable")) + }, + _ => { + let warn_msg = format!("Unexpected HTTP status {}", res.status()); + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)) + } } }, - Err(_e) => { - println!("Could not fetch IPv4"); - ret = false; - - return ret; // Early return since a fail on IPv4 means a likely fail on IPv6 - } + Err(e) => Err(e), } - - match get(ipv6uri) { - Ok(data) => { - if data.status() == 200 { - let ipv6 = match data.text() { - Ok(ip) => {ip}, - Err(_) => {panic!("Expected IP, found none")}, - }; - let stripped = match ipv6.trim_end().strip_suffix(ipv6_interface) { - Some(string) => string.to_string(), - None => ":".to_string(), - }; - self.ipv6base = stripped; - ret = true && ret; // Only set ret to true if previous ret is also true - } - else { - ret = false; - } - } - Err(_e) => { - println!("Could not fetch IPv6"); - ret = false; - } - } - - return ret; - } -} - -/// Loops over all DNS Entries in the config and calls an update on them -fn update_dns() { - let mut config = DnsConfig::new(); - config.load(); - - let mut ips = Ips::new(); - if ips.get(&config.ipv6_interface) { - println!("Current IPv4: {}", ips.ipv4); - println!("Current IPv6: {}{}", ips.ipv6base, &config.ipv6_interface); - - let mut change4 = ChangeTracker::new(); - - let mut change6 = ChangeTracker::new(); - - let mut update_results = Vec::::new(); - - // Iterator does not work here - for zone in &config.zones { - let error = zone.update(&mut change4, &mut change6, &ips, &config.api_key); - update_results.append(&mut vec!(error)); - if error { - break; // if one update fails all others are likely to fail as well - } - } - - if update_results.contains(&true) { - println!("DNS Update failed due to Network Error") - } - else { - println!("++++A Records+++"); - change4.print("simple"); - println!("++AAAA Records++"); - change6.print("simple") - } - } } fn main() { + dotenv().ok(); JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error"); log::set_max_level(LevelFilter::Info); + let mut ifaces = config::InterfaceConfig::load().unwrap(); + let mut zone_cfgs= config::ZoneConfig::load().unwrap(); + let mut now = Utc::now() - Duration::seconds(59); - let mut current = now; + let mut start = now; - let ifaces = config::InterfaceConfig::load().unwrap(); - let zone_cfgs= config::ZoneConfig::load().unwrap(); + let mut ips = match Addresses::new() { + Ok(ips) => ips, + Err(e) => panic!("{}", e) + }; + + loop { now = Utc::now(); - if now >= current + Duration::seconds(60) { - current = now; + if now >= start + Duration::seconds(60) { + start = now; - print!("\x1B[2J\x1B[1;1H"); - println!("Starting DNS Update at {} {}", now.format("%H:%M:%S"), now.timezone()); - update_dns(); + match config::InterfaceConfig::load() { + Ok(new) => ifaces = new, + Err(e) => { + let err_msg = format!("Unable to load ínterfaces.toml with error: {}", e); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + } + } + + match config::ZoneConfig::load() { + Ok(new) => zone_cfgs = new, + Err(e) => { + let err_msg = format!("Unable to load from zones.d with error: {}", e); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + } + } + + // Check if IPs changed + //if ips.check_new() { + if true { + let mut error = false; + for zone in &zone_cfgs { + let cf_zone = match CloudflareZone::new(&zone) { + Ok(data) => data, + Err(e) => { + let err_msg = format!("Cloudflare Token likely not set. Error: {}", e); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), + } + error = true; + continue + } + }; + + let cf_entries = match cf_zone.get_entries() { + Ok(entries) => entries, + Err(_) => { + error = true; + continue + } + }; + + for entry in &zone.entries { + let ipv6; + let ipv4; + match entry.r#type[..] { + [DnsRecordType::AAAA, DnsRecordType::A] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + error = true; + continue + } + }; + ipv4 = Some(ips.ipv4); + }, + [DnsRecordType::A, DnsRecordType::AAAA] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + error = true; + continue + } + }; + ipv4 = Some(ips.ipv4); + }, + [DnsRecordType::AAAA] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + error = true; + continue + } + }; + ipv4 = None; + }, + [DnsRecordType::A] => { + ipv6 = None; + ipv4 = Some(ips.ipv4); + }, + _ => { + let warn_msg = "Config contains unsupported type identifier"; + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + continue + } + } + + for r#type in &entry.r#type { + let cf_entry = cf_entries.iter().find(|cf_entry| { + cf_entry.name == entry.name && &cf_entry.r#type == r#type + }); + + if let Some(cf_entry) = cf_entry { + match cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4) { + Ok(_) => { + let info_msg = format!("Updated DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + match connected_to_journal() { + true => warn!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + }, + Err(_) => error = true, + }; + } + else { + match cf_zone.create(entry, r#type, ipv6, ipv4) { + Ok(_) => { + let info_msg = format!("Created DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + }, + Err(_) => error = true, + }; + } + } + + // handle return values + } + } + if !error { ips.check_new(true); } + } } else { - sleep(std::time::Duration::from_millis(500)); + sleep(std::time::Duration::from_millis(200)); } } } -- 2.39.2 From bd7514ea84b139388ecf6667b9e94e73efe41b36 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:18:58 +0100 Subject: [PATCH 15/24] Clippy linting changes --- src/cloudflare.rs | 33 +++++++++++++++++---------------- src/config.rs | 17 ++++++++--------- src/main.rs | 7 +++---- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/cloudflare.rs b/src/cloudflare.rs index 33ac704..ffe7bbb 100644 --- a/src/cloudflare.rs +++ b/src/cloudflare.rs @@ -60,7 +60,7 @@ impl CloudflareZone { match self.get(&endpoint) { Ok(response) => { - return if response.status().is_success() { + if response.status().is_success() { let entries = match response.json::() { Ok(data) => data, Err(e) => { @@ -97,9 +97,9 @@ impl CloudflareZone { pub(crate) fn update(&self, entry: &ZoneEntry, r#type: &DnsRecordType, id: &String, ipv6: Option, ipv4: Option) -> Result<(), ()> { let endpoint = format!("{}/zones/{}/dns_records/{}", API_BASE, self.id, id); - match r#type { + return match r#type { DnsRecordType::A => { - return if let Some(ip) = ipv4 { + if let Some(ip) = ipv4 { let json_body = self.create_body("A", &entry.name, ip.to_string().as_str()); match self.put(&endpoint, &json_body) { @@ -125,7 +125,7 @@ impl CloudflareZone { } }, DnsRecordType::AAAA => { - return if let Some(ip) = ipv6 { + if let Some(ip) = ipv6 { let json_body = self.create_body("AAAA", &entry.name, ip.to_string().as_str()); match self.put(&endpoint, &json_body) { @@ -156,7 +156,7 @@ impl CloudflareZone { true => warn!("[WARN] {warn_msg}"), false => println!("[WARN] {warn_msg}"), } - return Err(()) + Err(()) } } } @@ -164,9 +164,9 @@ impl CloudflareZone { pub(crate) fn create(&self, entry: &ZoneEntry, r#type: &DnsRecordType, ipv6: Option, ipv4: Option) -> Result<(), ()> { let endpoint = format!("{}/zones/{}/dns_records", API_BASE, self.id); - match r#type { + return match r#type { DnsRecordType::A => { - return if let Some(ip) = ipv4 { + if let Some(ip) = ipv4 { let json_body = self.create_body("A", &entry.name, ip.to_string().as_str()); match self.post(&endpoint, &json_body) { @@ -192,7 +192,7 @@ impl CloudflareZone { } }, DnsRecordType::AAAA => { - return if let Some(ip) = ipv6 { + if let Some(ip) = ipv6 { let json_body = self.create_body("AAAA", &entry.name, ip.to_string().as_str()); match self.post(&endpoint, &json_body) { @@ -223,7 +223,7 @@ impl CloudflareZone { true => warn!("[WARN] {warn_msg}"), false => println!("[WARN] {warn_msg}"), } - return Err(()) + Err(()) } } } @@ -275,16 +275,16 @@ impl CloudflareZone { true => error!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"), } - return Err(e) + Err(e) } } } - fn create_body(&self, r#type: &str, name: &String, ip: &str) -> HashMap { + fn create_body(&self, r#type: &str, name: &str, ip: &str) -> HashMap { let mut body = HashMap::new(); - body.insert("type".to_string(), r#type.to_string()); - body.insert("name".to_string(), name.clone()); - body.insert("content".to_string(), ip.to_string()); + body.insert("type".to_owned(), r#type.to_owned()); + body.insert("name".to_owned(), name.to_owned()); + body.insert("content".to_owned(), ip.to_owned()); body } @@ -303,14 +303,14 @@ impl CloudflareZone { }; match data.success { - true => return Ok(()), + true => Ok(()), false => { let err_msg = format!("Unexpected error while updating DNS record. Info: {:?}", data); match connected_to_journal() { true => error!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"), } - return Err(()) + Err(()) } } } else { @@ -325,6 +325,7 @@ impl CloudflareZone { } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy, Display, IntoStaticStr)] +#[allow(clippy::upper_case_acronyms)] pub(crate) enum DnsRecordType { A, AAAA, diff --git a/src/config.rs b/src/config.rs index 2858c8a..eecdf15 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,8 @@ use std::collections::HashMap; use std::error::Error; use std::fs; -use std::hash::Hash; use std::net::Ipv6Addr; -use ipnet::{IpAdd, IpBitAnd, IpBitOr, IpSub}; +use ipnet::{IpBitOr, IpSub}; use std::str::FromStr; use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; @@ -36,7 +35,7 @@ impl InterfaceConfig { pub(crate) fn full_v6(&self, interface_name: &String, host_v6: Ipv6Addr) -> Result { let host_range = Ipv6Addr::from(host_v6.saturating_sub(self.host_address)); let interface_address = match self.interfaces.get(interface_name) { - Some(address) => address.clone(), + Some(address) => *address, None => { let err_msg = "Malformed IP in interfaces.toml"; match connected_to_journal() { @@ -55,7 +54,7 @@ impl Default for InterfaceConfig { fn default() -> Self { InterfaceConfig { host_address: Ipv6Addr::from_str("::").expect("Malformed literal in code"), - interfaces: HashMap::from([(" ".to_string(), Ipv6Addr::from(0))]), + interfaces: HashMap::from([(" ".to_owned(), Ipv6Addr::from(0))]), } } } @@ -72,9 +71,9 @@ pub(crate) struct ZoneEntry { impl Default for ZoneEntry { fn default() -> Self { ZoneEntry { - name: " ".to_string(), + name: " ".to_owned(), r#type: vec![A, AAAA], - interface: " ".to_string(), + interface: " ".to_owned(), } } } @@ -131,9 +130,9 @@ impl ZoneConfig { impl Default for ZoneConfig { fn default() -> Self { ZoneConfig { - email: " ".to_string(), - name: " ".to_string(), - id: " ".to_string(), + email: " ".to_owned(), + name: " ".to_owned(), + id: " ".to_owned(), entries: vec![ZoneEntry::default()], } } diff --git a/src/main.rs b/src/main.rs index fcead42..91b4b33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ /*use cloudflare_old::{Instance, CloudflareDnsType};*/ use reqwest::blocking::get; -use serde_derive::{Deserialize, Serialize}; use std::{thread::{sleep}}; use std::error::Error; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -25,8 +24,8 @@ struct Addresses { impl Addresses { fn new() -> Result> { let mut ret = Self { - ipv4_uri: "https://am.i.mullvad.net/ip".to_string(), - ipv6_uri: "https://ipv6.am.i.mullvad.net/ip".to_string(), + ipv4_uri: "https://am.i.mullvad.net/ip".to_owned(), + ipv6_uri: "https://ipv6.am.i.mullvad.net/ip".to_owned(), ipv4: Ipv4Addr::new(0, 0, 0, 0), ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0) }; @@ -200,7 +199,7 @@ fn main() { if true { let mut error = false; for zone in &zone_cfgs { - let cf_zone = match CloudflareZone::new(&zone) { + let cf_zone = match CloudflareZone::new(zone) { Ok(data) => data, Err(e) => { let err_msg = format!("Cloudflare Token likely not set. Error: {}", e); -- 2.39.2 From 48e044652170f9fe02f264a57bd4dad540ddbead Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:24:35 +0100 Subject: [PATCH 16/24] Release Candidate 1.0.0-rc.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f53a033..abe8c2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ [[package]] name = "cloudflare-dns-updater" -version = "0.2.8" +version = "1.0.0-rc.1" dependencies = [ "chrono", "confy", diff --git a/Cargo.toml b/Cargo.toml index c6d37d4..8fcab3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-dns-updater" -version = "0.2.8" +version = "1.0.0-rc.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -- 2.39.2 From 285ed25eafd666f5eae92b3885d70346205683bd Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 04:28:00 +0100 Subject: [PATCH 17/24] Fix Release CI for rc tags --- .forgejo/workflows/build+release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml index 300b906..743a7f0 100644 --- a/.forgejo/workflows/build+release.yml +++ b/.forgejo/workflows/build+release.yml @@ -5,7 +5,7 @@ on: push: tags: - '[0-9]+.[0-9]+.[0-9]+' - - '[0-9]+.[0-9]+.[0-9]+rc[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+' jobs: test: runs-on: docker -- 2.39.2 From e9ca98692686de5cae035bb7f02edbd6630c960b Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 18:23:50 +0100 Subject: [PATCH 18/24] Remove excessive node install in actions --- .forgejo/workflows/build+release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml index 743a7f0..33b38f2 100644 --- a/.forgejo/workflows/build+release.yml +++ b/.forgejo/workflows/build+release.yml @@ -47,10 +47,7 @@ jobs: if: success() runs-on: docker container: forgejo.neshweb.net/ci-docker-images/rust-node:latest - steps: - - - name: Installing Node - run: apt update && apt install -y nodejs + steps: - name: Checking Out Repository Code uses: https://code.forgejo.org/actions/checkout@v3 -- 2.39.2 From 293f0af30a635446674f5d698877bff55cf0b253 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 18:24:04 +0100 Subject: [PATCH 19/24] Remove debug statement in main --- src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 91b4b33..988247e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -194,9 +194,7 @@ fn main() { } } - // Check if IPs changed - //if ips.check_new() { - if true { + if ips.check_new(false) { let mut error = false; for zone in &zone_cfgs { let cf_zone = match CloudflareZone::new(zone) { -- 2.39.2 From 6714f447de743f02d15ad5cd618c8bf3087762ee Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 18:24:29 +0100 Subject: [PATCH 20/24] Release Candidate 1.0.0-rc.2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abe8c2f..4462b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ [[package]] name = "cloudflare-dns-updater" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" dependencies = [ "chrono", "confy", diff --git a/Cargo.toml b/Cargo.toml index 8fcab3b..f0223bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-dns-updater" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -- 2.39.2 From f969e21418c02ce2b03e50b89b83958216b9b980 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 18:53:22 +0100 Subject: [PATCH 21/24] Bugfix: DNS Updater would not work for freshly created DNS records or records with outdated info --- src/cloudflare.rs | 2 +- src/main.rs | 227 +++++++++++++++++++++++----------------------- 2 files changed, 115 insertions(+), 114 deletions(-) diff --git a/src/cloudflare.rs b/src/cloudflare.rs index ffe7bbb..8056993 100644 --- a/src/cloudflare.rs +++ b/src/cloudflare.rs @@ -354,5 +354,5 @@ pub(crate) struct CloudflareDnsRecord { pub(crate) id: String, pub(crate) name: String, pub(crate) r#type: DnsRecordType, - content: String + pub(crate) content: String } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 988247e..e3b844e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,9 +57,7 @@ impl Addresses { Ok(ret) } - fn check_new(&mut self, update: bool) -> bool { - let mut new = false; - + fn update(&mut self) { match self.get_v4() { Ok(ip) => { if ip != self.ipv4 { @@ -68,8 +66,7 @@ impl Addresses { true => info!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"), } - new = true; - if update { self.ipv4 = ip } + self.ipv4 = ip; } } Err(e) => { @@ -89,8 +86,7 @@ impl Addresses { true => info!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"), } - new = true; - if update { self.ipv6 = ip } + self.ipv6 = ip; } } Err(e) => { @@ -101,8 +97,6 @@ impl Addresses { } } } - - new } fn get_v4(&self) -> Result { @@ -194,113 +188,120 @@ fn main() { } } - if ips.check_new(false) { - let mut error = false; - for zone in &zone_cfgs { - let cf_zone = match CloudflareZone::new(zone) { - Ok(data) => data, - Err(e) => { - let err_msg = format!("Cloudflare Token likely not set. Error: {}", e); - match connected_to_journal() { - true => error!("[ERROR] {err_msg}"), - false => eprintln!("[ERROR] {err_msg}"), - } - error = true; - continue + ips.update(); + for zone in &zone_cfgs { + let cf_zone = match CloudflareZone::new(zone) { + Ok(data) => data, + Err(e) => { + let err_msg = format!("Cloudflare Token likely not set. Error: {}", e); + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => eprintln!("[ERROR] {err_msg}"), } - }; - - let cf_entries = match cf_zone.get_entries() { - Ok(entries) => entries, - Err(_) => { - error = true; - continue - } - }; - - for entry in &zone.entries { - let ipv6; - let ipv4; - match entry.r#type[..] { - [DnsRecordType::AAAA, DnsRecordType::A] => { - ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { - Ok(ip) => Some(ip), - Err(_) => { - error = true; - continue - } - }; - ipv4 = Some(ips.ipv4); - }, - [DnsRecordType::A, DnsRecordType::AAAA] => { - ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { - Ok(ip) => Some(ip), - Err(_) => { - error = true; - continue - } - }; - ipv4 = Some(ips.ipv4); - }, - [DnsRecordType::AAAA] => { - ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { - Ok(ip) => Some(ip), - Err(_) => { - error = true; - continue - } - }; - ipv4 = None; - }, - [DnsRecordType::A] => { - ipv6 = None; - ipv4 = Some(ips.ipv4); - }, - _ => { - let warn_msg = "Config contains unsupported type identifier"; - match connected_to_journal() { - true => warn!("[WARN] {warn_msg}"), - false => println!("[WARN] {warn_msg}"), - } - continue - } - } - - for r#type in &entry.r#type { - let cf_entry = cf_entries.iter().find(|cf_entry| { - cf_entry.name == entry.name && &cf_entry.r#type == r#type - }); - - if let Some(cf_entry) = cf_entry { - match cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4) { - Ok(_) => { - let info_msg = format!("Updated DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); - match connected_to_journal() { - true => warn!("[INFO] {info_msg}"), - false => println!("[INFO] {info_msg}"), - } - }, - Err(_) => error = true, - }; - } - else { - match cf_zone.create(entry, r#type, ipv6, ipv4) { - Ok(_) => { - let info_msg = format!("Created DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); - match connected_to_journal() { - true => info!("[INFO] {info_msg}"), - false => println!("[INFO] {info_msg}"), - } - }, - Err(_) => error = true, - }; - } - } - - // handle return values + continue } + }; + + let cf_entries = match cf_zone.get_entries() { + Ok(entries) => entries, + Err(_) => { + continue + } + }; + + for entry in &zone.entries { + let ipv6; + let ipv4; + match entry.r#type[..] { + [DnsRecordType::AAAA, DnsRecordType::A] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + continue + } + }; + ipv4 = Some(ips.ipv4); + }, + [DnsRecordType::A, DnsRecordType::AAAA] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + continue + } + }; + ipv4 = Some(ips.ipv4); + }, + [DnsRecordType::AAAA] => { + ipv6 = match ifaces.full_v6(&entry.interface, ips.ipv6) { + Ok(ip) => Some(ip), + Err(_) => { + continue + } + }; + ipv4 = None; + }, + [DnsRecordType::A] => { + ipv6 = None; + ipv4 = Some(ips.ipv4); + }, + _ => { + let warn_msg = "Config contains unsupported type identifier"; + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } + continue + } + } + + for r#type in &entry.r#type { + let cf_entry = cf_entries.iter().find(|cf_entry| { + cf_entry.name == entry.name && &cf_entry.r#type == r#type + }); + + match cf_entry.unwrap().r#type { + DnsRecordType::A => { + let cf_ip = Ipv4Addr::from_str(cf_entry.unwrap().content.as_str()).expect("Cloudflare return should always be valid IP"); + if Some(cf_ip) == ipv4 { + continue + } + }, + DnsRecordType::AAAA => { + let cf_ip = Ipv6Addr::from_str(cf_entry.unwrap().content.as_str()).expect("Cloudflare return should always be valid IP"); + if Some(cf_ip) == ipv6 { + continue + } + }, + _ => {}, + } + + if let Some(cf_entry) = cf_entry { + match cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4) { + Ok(_) => { + let info_msg = format!("Updated DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + match connected_to_journal() { + true => warn!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + }, + Err(_) => {}, + }; + } + else { + match cf_zone.create(entry, r#type, ipv6, ipv4) { + Ok(_) => { + let info_msg = format!("Created DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + }, + Err(_) => {}, + }; + } + } + // handle return values } - if !error { ips.check_new(true); } } } else { -- 2.39.2 From 660b4efa26d87f8b571e22ed86ee05ec949626a0 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 26 Dec 2023 18:53:46 +0100 Subject: [PATCH 22/24] Release Candidate 1.0.0-rc.3 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4462b9f..710fb91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ [[package]] name = "cloudflare-dns-updater" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" dependencies = [ "chrono", "confy", diff --git a/Cargo.toml b/Cargo.toml index f0223bb..65c58ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-dns-updater" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -- 2.39.2 From 4adfc7efb2eb2d046027b257c9fe55cf052e9b1a Mon Sep 17 00:00:00 2001 From: Neshura Date: Wed, 27 Dec 2023 13:34:30 +0100 Subject: [PATCH 23/24] Split DNS info depending on record type. See #27 --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index e3b844e..98997b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -278,7 +278,7 @@ fn main() { if let Some(cf_entry) = cf_entry { match cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4) { Ok(_) => { - let info_msg = format!("Updated DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); match connected_to_journal() { true => warn!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"), @@ -290,7 +290,7 @@ fn main() { else { match cf_zone.create(entry, r#type, ipv6, ipv4) { Ok(_) => { - let info_msg = format!("Created DNS Record(s) for entry '{}' in zone '{}'", entry.name, zone.name); + let info_msg = format!("Created {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); match connected_to_journal() { true => info!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"), -- 2.39.2 From 251771f3c31cd01e0cc5b1ece6b0cbbe646afa54 Mon Sep 17 00:00:00 2001 From: Neshura Date: Wed, 27 Dec 2023 13:39:02 +0100 Subject: [PATCH 24/24] Clippy changes --- src/main.rs | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 98997b4..ddbb5ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -276,27 +276,19 @@ fn main() { } if let Some(cf_entry) = cf_entry { - match cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4) { - Ok(_) => { - let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); - match connected_to_journal() { - true => warn!("[INFO] {info_msg}"), - false => println!("[INFO] {info_msg}"), - } - }, - Err(_) => {}, - }; + if cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4).is_ok() { + let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); + match connected_to_journal() { + true => warn!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } + } } - else { - match cf_zone.create(entry, r#type, ipv6, ipv4) { - Ok(_) => { - let info_msg = format!("Created {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); - match connected_to_journal() { - true => info!("[INFO] {info_msg}"), - false => println!("[INFO] {info_msg}"), - } - }, - Err(_) => {}, + else if cf_zone.create(entry, r#type, ipv6, ipv4).is_ok() { + let info_msg = format!("Created {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name); + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), }; } } -- 2.39.2