Compare commits

..

No commits in common. "main" and "1.1.1-rc.3" have entirely different histories.

10 changed files with 83 additions and 191 deletions

View file

@ -1,4 +1,4 @@
name: 'Build and release binary file and packages' name: 'Build and Release Binary File'
author: 'Neshura' author: 'Neshura'
on: on:
@ -63,7 +63,7 @@ jobs:
name: Bundle .deb package name: Bundle .deb package
run: | run: |
cargo deb cargo deb
DEBIAN_REF=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " " | tr - \~) DEBIAN_REF=$(echo ${{ github.ref_name }} | tr - \~)
echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ") DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
@ -76,7 +76,7 @@ jobs:
path: dist path: dist
if-no-files-found: error if-no-files-found: error
upload-generic-package: upload-release:
needs: build needs: build
if: success() if: success()
runs-on: docker runs-on: docker
@ -89,55 +89,23 @@ jobs:
run: | run: |
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \ echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \ --upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64' https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/chellaris-rust-api-linux-amd64'
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \ curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \ --upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64 https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/chellaris-rust-api-linux-amd64
upload-debian-package:
needs: build
if: success()
runs-on: docker
steps:
- -
name: Downloading All Build Artifacts name: Upload Debian Package
uses: actions/download-artifact@v3
-
name: Upload Debian Package to staging
run: | run: |
source release_blobs/build.env source release_blobs/build.env
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \ echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload'
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \ --upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload
-
name: Upload Debian Package to main
if: (! contains(github.ref_name, '-rc'))
run: |
source release_blobs/build.env
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload' https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload'
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \ curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
--upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \ --upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload
create-release:
needs: build
if: success()
runs-on: docker
steps:
-
name: Downloading All Build Artifacts
uses: actions/download-artifact@v3
-
name: Filter out env files
run: rm release_blobs/build.env
- -
name: Release New Version name: Release New Version
uses: actions/forgejo-release@v2 uses: actions/forgejo-release@v1
with: with:
direction: upload direction: upload
url: https://forgejo.neshweb.net url: https://forgejo.neshweb.net

View file

@ -1,67 +0,0 @@
name: 'Build binary file and bundle packages'
author: 'Neshura'
on:
pull_request:
branches:
- main
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: 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
build:
needs: test
if: success()
runs-on: docker
container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
steps:
-
name: Checking Out Repository Code
uses: https://code.forgejo.org/actions/checkout@v3
-
name: Prepare build environment
run: mkdir dist
-
name: Compiling To Linux Target
run: |
cargo build -r
mv target/release/${{ github.event.repository.name }} dist/${{ github.event.repository.name }}-linux-amd64
-
name: Bundle .deb package
run: |
cargo deb
DEBIAN_REF=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " " | tr - \~)
echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
mv target/debian/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb dist/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb
-
name: Uploading Build Artifact
uses: actions/upload-artifact@v3
with:
name: release_blobs
path: dist
if-no-files-found: error

1
.gitignore vendored
View file

@ -4,5 +4,6 @@ venv/
.idea/ .idea/
.vscode/ .vscode/
/.env
/interfaces.toml /interfaces.toml
/zones.d /zones.d

29
Cargo.lock generated
View file

@ -114,7 +114,7 @@ dependencies = [
[[package]] [[package]]
name = "cloudflare-dns-updater" name = "cloudflare-dns-updater"
version = "1.1.9" version = "1.1.1-rc.3"
dependencies = [ dependencies = [
"chrono", "chrono",
"confy", "confy",
@ -620,7 +620,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.43",
] ]
[[package]] [[package]]
@ -821,7 +821,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.43",
] ]
[[package]] [[package]]
@ -868,15 +868,26 @@ dependencies = [
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.25.3" version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn", "syn 1.0.109",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
] ]
[[package]] [[package]]
@ -951,7 +962,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.43",
] ]
[[package]] [[package]]
@ -1129,7 +1140,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.43",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1163,7 +1174,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.43",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View file

@ -1,7 +1,7 @@
[package] [package]
authors = ["Neshura"] authors = ["Neshura"]
name = "cloudflare-dns-updater" name = "cloudflare-dns-updater"
version = "1.1.9" version = "1.1.1-rc.3"
edition = "2021" edition = "2021"
description = "Application for automatically updating Cloudflare DNS records" description = "Application for automatically updating Cloudflare DNS records"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
@ -10,14 +10,6 @@ license = "GPL-3.0-or-later"
extended-description = "Application for automatically updating Cloudflare DNS records" extended-description = "Application for automatically updating Cloudflare DNS records"
maintainer-scripts = "debian/" maintainer-scripts = "debian/"
revision = "1" revision = "1"
depends = ["libc6", "libssl3", "systemd"]
assets = [
[
"target/release/cloudflare-dns-updater",
"/usr/local/bin/cloudflare-dns-updater",
"755",
]
]
systemd-units = { enable = false } systemd-units = { enable = false }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -28,7 +20,7 @@ reqwest = { version = "^0.11.14", features = ["blocking", "json"] }
serde = "^1.0.152" serde = "^1.0.152"
serde_derive = "^1.0.152" serde_derive = "^1.0.152"
serde_json = "^1.0.93" serde_json = "^1.0.93"
strum_macros = "^0.25.3" strum_macros = "^0.24.3"
log = "^0.4.20" log = "^0.4.20"
systemd-journal-logger = "^2.1.1" systemd-journal-logger = "^2.1.1"
confy = "^0.5.1" confy = "^0.5.1"

View file

@ -7,10 +7,10 @@
The application necessarily requires a valid Cloudflare API Token. The application necessarily requires a valid Cloudflare API Token.
Further the application must be located in the same network as the configured zones. Further the application must be located in the same network as the configured zones.
The actual configuration happens in three or more files located in `~/.config/cloudflare-dns-updater/`: The actual configuration happens in three or more files:
`config.toml` contains general configuration parameters for the application `config.toml` contains general configuration parameters for the application
`interfaces.toml` contains all IPv6 interfaces available/used by the zone config files. `interfaces.toml` contains all IPv6 interfaces available/used by the zone config files.
`.toml` files in `zones.d` contain settings for individual zones. `.toml` files in `zone.d` contain settings for individual zones.
Example: Example:
@ -29,7 +29,7 @@ host_address = "::edcb:a098:7654:3210"
example-interface = "::0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host example-interface = "::0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host
``` ```
*zones.d/example.org.toml* *zone.d/example.org.toml*
```toml ```toml
email = "owner@example.org" # Email of User owning the Zone email = "owner@example.org" # Email of User owning the Zone
zone = "example.org" # Zone Name zone = "example.org" # Zone Name
@ -42,10 +42,4 @@ interface = "example-interface" # Only required on type values 6 and 10
``` ```
## Debian Repository ## Debian Repository
TODO!
Currently supported:
- Debian 12 'Bookworm'
Includes systemd system and user unit files
For more details see [the package registry](https://forgejo.neshweb.net/Neshura/-/packages/debian/cloudflare-dns-updater)

View file

@ -5,7 +5,6 @@ After=network-online.target
[Service] [Service]
Type=simple Type=simple
User=%i
ExecStart=/usr/local/bin/cloudflare-dns-updater ExecStart=/usr/local/bin/cloudflare-dns-updater
Restart=always Restart=always
RestartSec=3 RestartSec=3

View file

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::env::VarError;
use std::error::Error; use std::error::Error;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use log::{error, warn}; use log::{error, warn};
@ -32,14 +33,14 @@ pub(crate) struct CloudflareZone {
} }
impl CloudflareZone { impl CloudflareZone {
pub(crate) fn new(zone: &ZoneConfig, config: &AppConfig) -> Self { pub(crate) fn new(zone: &ZoneConfig, config: &AppConfig) -> Result<Self, VarError> {
let key = config.cloudflare_api_token.clone(); let key = config.cloudflare_api_token.clone();
Self { Ok(Self {
name: zone.name.clone(), name: zone.name.clone(),
email: zone.email.clone(), email: zone.email.clone(),
key, key,
id: zone.id.clone(), id: zone.id.clone(),
} })
} }
fn generate_auth_headers(&self) -> HeaderMap { fn generate_auth_headers(&self) -> HeaderMap {
@ -64,7 +65,7 @@ impl CloudflareZone {
let entries = match response.json::<CloudflareApiResults>() { let entries = match response.json::<CloudflareApiResults>() {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("Unable to parse API response: {e}"); let err_msg = format!("Unable to parse API response. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -75,7 +76,7 @@ impl CloudflareZone {
Ok(entries.result) Ok(entries.result)
} else { } else {
let err_msg = format!("Unable to fetch Cloudflare Zone Entries for {}: {}",self.name ,response.status()); let err_msg = format!("Unable to fetch Cloudflare Zone Entries for {}. Error: {}",self.name ,response.status());
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -84,7 +85,7 @@ impl CloudflareZone {
} }
} }
Err(e) => { Err(e) => {
let err_msg = format!("Unable to access Cloudflare API: {e}"); let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -107,7 +108,7 @@ impl CloudflareZone {
self.validate_response(response) self.validate_response(response)
}, },
Err(e) => { Err(e) => {
let err_msg = format!("Unable to access Cloudflare API: {e}"); let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -133,7 +134,7 @@ impl CloudflareZone {
self.validate_response(response) self.validate_response(response)
}, },
Err(e) => { Err(e) => {
let err_msg = format!("Unable to access Cloudflare API: {e}"); let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -174,7 +175,7 @@ impl CloudflareZone {
self.validate_response(response) self.validate_response(response)
}, },
Err(e) => { Err(e) => {
let err_msg = format!("Unable to access Cloudflare API: {e}"); let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -200,7 +201,7 @@ impl CloudflareZone {
self.validate_response(response) self.validate_response(response)
}, },
Err(e) => { Err(e) => {
let err_msg = format!("Unable to access Cloudflare API: {e}"); let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -270,7 +271,7 @@ impl CloudflareZone {
match Url::parse(input) { match Url::parse(input) {
Ok(url) => Ok(url), Ok(url) => Ok(url),
Err(e) => { Err(e) => {
let err_msg = format!("Unable to parse URL: {}", e); let err_msg = format!("Unable to parse URL. Error: {}", e);
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -293,7 +294,7 @@ impl CloudflareZone {
let data = match response.json::<CloudflareApiResult>() { let data = match response.json::<CloudflareApiResult>() {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("Unable to parse API response: {e}"); let err_msg = format!("Unable to parse API response. Error: {e}");
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -305,7 +306,7 @@ impl CloudflareZone {
match data.success { match data.success {
true => Ok(()), true => Ok(()),
false => { false => {
let err_msg = format!("Unexpected error while updating DNS record: {:?}", data); let err_msg = format!("Unexpected error while updating DNS record. Info: {:?}", data);
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -314,7 +315,7 @@ impl CloudflareZone {
} }
} }
} else { } else {
let err_msg = format!("Unable to post/put Cloudflare DNS entry: {}", response.status()); let err_msg = format!("Unable to post/put Cloudflare DNS entry. Error: {}", response.status());
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),

View file

@ -37,7 +37,7 @@ impl InterfaceConfig {
let interface_address = match self.interfaces.get(interface_name) { let interface_address = match self.interfaces.get(interface_name) {
Some(address) => *address, Some(address) => *address,
None => { None => {
let err_msg = format!("Malformed or missing IP in interfaces.toml for interface {}", interface_name); let err_msg = "Malformed IP in interfaces.toml";
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => eprintln!("[ERROR] {err_msg}"), false => eprintln!("[ERROR] {err_msg}"),
@ -142,6 +142,7 @@ impl Default for ZoneConfig {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub(crate) struct AppConfig { pub(crate) struct AppConfig {
#[serde(alias="cf_api_token")]
pub(crate) cloudflare_api_token: String, pub(crate) cloudflare_api_token: String,
pub(crate) check_interval_seconds: Option<u16>, pub(crate) check_interval_seconds: Option<u16>,
pub(crate) uptime_url: Option<String>, pub(crate) uptime_url: Option<String>,

View file

@ -24,8 +24,8 @@ struct Addresses {
impl Addresses { impl Addresses {
fn new() -> Result<Self, Box<dyn Error>> { fn new() -> Result<Self, Box<dyn Error>> {
let mut ret = Self { let mut ret = Self {
ipv4_uri: "http://ip4only.me/api/".to_owned(), ipv4_uri: "https://am.i.mullvad.net/ip".to_owned(),
ipv6_uri: "http://ip6only.me/api/".to_owned(), ipv6_uri: "https://ipv6.am.i.mullvad.net/ip".to_owned(),
ipv4: Ipv4Addr::new(0, 0, 0, 0), ipv4: Ipv4Addr::new(0, 0, 0, 0),
ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0) ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)
}; };
@ -61,28 +61,19 @@ impl Addresses {
match self.get_v4() { match self.get_v4() {
Ok(ip) => { Ok(ip) => {
if ip != self.ipv4 { if ip != self.ipv4 {
if ip == Ipv4Addr::new(0,0,0,0) { let info_msg = format!("IPv4 changed from '{}' to '{}'", self.ipv4, ip);
let warn_msg = "'0.0.0.0' detected as new IPv4, skipping changes".to_owned(); match connected_to_journal() {
match connected_to_journal() { true => info!("[INFO] {info_msg}"),
true => warn!("[WARN] {warn_msg}"), false => println!("[INFO] {info_msg}"),
false => println!("[WARN] {warn_msg}"),
}
}
else {
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}"),
}
self.ipv4 = ip;
} }
self.ipv4 = ip;
} }
} }
Err(e) => { Err(e) => {
let error_msg = format!("Unable to fetch IPv4 from '{}': {}", self.ipv4_uri, e); let warn_msg = format!("Unable to fetch IPv4 from '{}'. Error: {}", self.ipv4_uri, e);
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {error_msg}"), true => warn!("[WARN] {warn_msg}"),
false => eprintln!("[ERROR] {error_msg}"), false => println!("[WARN] {warn_msg}"),
} }
} }
} }
@ -90,28 +81,19 @@ impl Addresses {
match self.get_v6() { match self.get_v6() {
Ok(ip) => { Ok(ip) => {
if ip != self.ipv6 { if ip != self.ipv6 {
if ip == Ipv6Addr::new(0,0,0,0,0,0,0,0) { let info_msg = format!("IPv6 changed from '{}' to '{}'", self.ipv6, ip);
let warn_msg = "'::' detected as new IPv6, skipping changes".to_owned(); match connected_to_journal() {
match connected_to_journal() { true => info!("[INFO] {info_msg}"),
true => warn!("[WARN] {warn_msg}"), false => println!("[INFO] {info_msg}"),
false => println!("[WARN] {warn_msg}"),
}
}
else {
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}"),
}
self.ipv6 = ip;
} }
self.ipv6 = ip;
} }
} }
Err(e) => { Err(e) => {
let error_msg = format!("Unable to fetch IPv6 from '{}': {}", self.ipv6_uri, e); let warn_msg = format!("Unable to fetch IPv6 from '{}'. Error: {}", self.ipv6_uri, e);
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {error_msg}"), true => warn!("[WARN] {warn_msg}"),
false => eprintln!("[ERROR] {error_msg}"), false => println!("[WARN] {warn_msg}"),
} }
} }
} }
@ -122,7 +104,7 @@ impl Addresses {
Ok(res) => { Ok(res) => {
match res.status() { match res.status() {
StatusCode::OK => { StatusCode::OK => {
let ip_string = res.text().expect("Returned data should always contain text").trim_end().split(',').collect::<Vec<&str>>()[1].to_owned(); 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")) Ok(Ipv4Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
}, },
_ => { _ => {
@ -145,7 +127,7 @@ impl Addresses {
Ok(res) => { Ok(res) => {
match res.status() { match res.status() {
StatusCode::OK => { StatusCode::OK => {
let ip_string: String = res.text().expect("Returned data should always contain text").trim_end().split(',').collect::<Vec<&str>>()[1].to_owned(); 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")) Ok(Ipv6Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
}, },
_ => { _ => {
@ -577,7 +559,17 @@ fn main() {
ips.update(); ips.update();
for zone in &zone_cfgs { for zone in &zone_cfgs {
let cf_zone = CloudflareZone::new(zone, &config); let cf_zone = match CloudflareZone::new(zone, &config) {
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}"),
}
continue
}
};
let cf_entries = match cf_zone.get_entries() { let cf_entries = match cf_zone.get_entries() {
Ok(entries) => entries, Ok(entries) => entries,
@ -656,7 +648,7 @@ fn main() {
if cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4).is_ok() { 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); let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name);
match connected_to_journal() { match connected_to_journal() {
true => info!("[INFO] {info_msg}"), true => warn!("[INFO] {info_msg}"),
false => println!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"),
} }
} }