From e76c82492fc549fc134dad1b2b7c284490914df1 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 01:10:39 +0100 Subject: [PATCH 01/31] Rewrite in Rust --- .gitignore | 2 + Cargo.lock | 1021 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 + src/cloudflare/mod.rs | 191 ++++++++ src/main.rs | 175 +++++++ 5 files changed, 1402 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/cloudflare/mod.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 2388456..04a070b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # Default ignores +target/ venv/ .idea/ .vscode/ +config.json diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..51b3541 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1021 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cloudflare-dns-updater" +version = "0.1.0" +dependencies = [ + "reqwest", + "serde", + "serde_derive", + "serde_json", + "strum_macros", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +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" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[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]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +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", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8cea999 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cloudflare-dns-updater" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +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" diff --git a/src/cloudflare/mod.rs b/src/cloudflare/mod.rs new file mode 100644 index 0000000..1d20413 --- /dev/null +++ b/src/cloudflare/mod.rs @@ -0,0 +1,191 @@ +use reqwest::{blocking::{Response, Client}, Url}; +use serde_derive::{Deserialize, Serialize}; +use reqwest::header::{HeaderMap, HeaderValue}; +use std::{string::String, collections::HashMap}; +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)] +#[derive(Display, IntoStaticStr)] +pub enum CloudflareDnsType { + #[strum(serialize = "A")] + A, + #[strum(serialize = "AAAA")] + AAAA, + TXT, + MX +} + +#[derive(Serialize, Deserialize)] +pub struct CloudflareDnsEntry { + id: String, + 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 + pub fn load_entries(&mut self, email: &String, zone_id: &String) { + self.dns_email = email.clone(); + let endpoint = format!("{}zones/{}/dns_records", API_BASE, zone_id); + + let api_response = self.get(&endpoint); + let entries = match api_response.json::() { + Ok(data) => {data}, + Err(e) => panic!("{:#?}", e) + }; + + for entry in entries.result { + self.dns_entries.append(&mut vec!(entry)); + } + } + + /// Shorthand for Cloudflare API Requests + /// + /// Automatically appends Auth Headers and parses the URL + /// + /// * `url` : URL for the request, will be parsed to [`Url`] + /// * `email` : E-Mail Address associated with the Zone, required for [`generate_headers()`] + fn get(&self, url: &str) -> Response { + let url_parsed = match Url::parse(url) { + Ok(url) => {url}, + Err(e) => panic!("{:#?}", e) + }; + + let client = Client::new(); + let res = client.get(url_parsed) + .headers(self.generate_headers()) + .send(); + + let response = match res { + Ok(res) => {res}, + Err(e) => panic!("{:#?}", e) + }; + + response + } + + fn put(&self, url: &str, data: &HashMap<&str, &str>) -> Response { + let url_parsed = match Url::parse(url) { + Ok(url) => {url}, + Err(e) => panic!("{:#?}", e) + }; + + let client = Client::new(); + let res = client.put(url_parsed) + .headers(self.generate_headers()) + .json(data) + .send(); + + let response = match res { + Ok(res) => {res}, + Err(e) => panic!("{:#?}", e) + }; + + response + } + + fn post(&self, url: &str, data: &HashMap<&str, &str>) -> Response { + let url_parsed = match Url::parse(url) { + Ok(url) => {url}, + Err(e) => panic!("{:#?}", e) + }; + + let client = Client::new(); + let res = client.post(url_parsed) + .headers(self.generate_headers()) + .json(data) + .send(); + + let response = match res { + Ok(res) => {res}, + Err(e) => panic!("{:#?}", e) + }; + + response + } + + pub fn update_entry(&self, entry: &CloudflareDnsEntry, new_ip: &String) -> bool { + 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"); + + let api_response = self.put(&endpoint, &json_body); + + let data = api_response.json::().unwrap(); + + let ret = data["success"].as_bool().unwrap(); + + ret + } + + pub fn create_entry(&self, zone_id: &String, r#type: &str, name: &str, ip: &String) -> bool { + 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"); + + let api_response = self.post(&endpoint, &json_body); + + let data = api_response.json::().unwrap(); + + let ret = data["success"].as_bool().unwrap(); + + ret + } + + /// 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 + } + +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bb16a87 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,175 @@ +use serde_derive::{Deserialize, Serialize}; +use reqwest::blocking::get; +use std::fs; +mod cloudflare; + +#[derive(Serialize, Deserialize)] +struct DnsEntry { + name: String, + type4: bool, + type6: bool, + interface: Option +} + +#[derive(Serialize, Deserialize)] +struct DnsZone { + email: String, + name: String, + id: String, + dns_entries: Vec +} + +#[derive(Serialize, Deserialize)] +struct DnsConfig { + ipv6_interface: String, + 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) + }; + *self = config_parse; + } +} + +struct Ips { + ipv4: String, + ipv6base: String +} + +impl Ips { + fn new() -> Ips { + Ips { + ipv4: "0.0.0.0".to_string(), + ipv6base: ":".to_string() + } + } + + fn get(&mut self, ipv6_interface: &str) { + let ipv4uri = "https://am.i.mullvad.net/ip"; + let ipv6uri = "https://ipv6.am.i.mullvad.net/ip"; + + let response = get(ipv4uri); + + match response { + Ok(data) => self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(), + Err(e) => panic!("{:#?}", e) + } + + let response = get(ipv6uri); + + match response { + Ok(data) => { + let ipv6 = data.text().expect(":"); + let stripped = match ipv6.strip_suffix(ipv6_interface) { + Some(string) => {string.to_string()}, + None => {":".to_string()} + }; + self.ipv6base = stripped; + }, + Err(e) => panic!("{:#?}", e) + } + } +} + +fn main() { + + let mut config = DnsConfig::new(); + config.load(); + + let mut ips = Ips::new(); + ips.get(&config.ipv6_interface); + + let mut created4: u16 = 0; + let mut updated4: u16 = 0; + let mut unchanged4: u16 = 0; + let mut error4: u16 = 0; + + let mut created6: u16 = 0; + let mut updated6: u16 = 0; + let mut unchanged6: u16 = 0; + let mut error6: u16 = 0; + + for zone in config.zones { + let mut agent = cloudflare::Instance::new(&config.api_key); + + agent.load_entries(&zone.email, &zone.id); + + + for entry in zone.dns_entries { + let mut found4 = false; + let mut found6 = false; + + for cloudflare_entry in &agent.dns_entries { + + if cloudflare_entry.is_equal(&entry.name) { + if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::A && entry.type4 { + found4 = true; + if cloudflare_entry.is_ip_new(&ips.ipv4) { + let success = agent.update_entry(cloudflare_entry, &ips.ipv4); + if success { updated4 += 1 } else { error4 += 1 } + } + else { + unchanged4 += 1; + } + } + + if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::AAAA && entry.type6 { + found6 = true; + let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); + + if cloudflare_entry.is_ip_new(&ipv6) { + let success = agent.update_entry(cloudflare_entry, &ipv6); + if success { updated6 += 1 } else { error6 += 1 } + } + else { + unchanged6 += 1; + } + } + + } + + //println!("{:#?}", entry.subdomain); // DEBUG + } + + if !found4 && entry.type4 { + let success = agent.create_entry(&zone.id, "A", &entry.name, &ips.ipv4); + if success { created4 += 1 }; + } + + if !found6 && entry.type6 { + let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); + let success = agent.create_entry(&zone.id, "AAAA", &entry.name, &ipv6); + if success { created6 += 1 }; + } + } + + //println!("{:#?}", zone.domain); // DEBUG + } + println!("++++A Records+++"); + println!("{} created", created4); + println!("{} updated", updated4); + println!("{} unchanged", unchanged4); + println!("{} errors", error4); + println!("++AAAA Records++"); + println!("{} created", created6); + println!("{} updated", updated6); + println!("{} unchanged", unchanged6); + println!("{} errors", error6); + +} From f2d3fc5fb200b0ee793b7f274d940c29fc775493 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 01:10:55 +0100 Subject: [PATCH 02/31] Removed all python files except config --- cloudflare_script.py | 100 ------------------------------------------- config.ini | 5 --- requirements.txt | 1 - set_ip.sh | 23 ---------- 4 files changed, 129 deletions(-) delete mode 100644 cloudflare_script.py delete mode 100644 config.ini delete mode 100644 requirements.txt delete mode 100644 set_ip.sh diff --git a/cloudflare_script.py b/cloudflare_script.py deleted file mode 100644 index f192e49..0000000 --- a/cloudflare_script.py +++ /dev/null @@ -1,100 +0,0 @@ -import sys -import ipaddress -from configparser import ConfigParser -import json -import copy -import CloudFlare - - -class DNSUpdater: - def __init__(self, args): - # Parse config - config = ConfigParser() - config.read('config.ini') - - self.HOSTNAME = config['server']['HOSTNAME'] - __TOKEN = config['cloudflare']['TOKEN'] - - # Parse args - try: - ipversion = args[1].lstrip('-') - ipaddress_new = args[2] - except IndexError: - ipaddress_new = '' - ipversion = 'skip' - - match ipversion: - case '4': self.IPVER = 'ipv4' - case '6': self.IPVER = 'ipv6' - case 'skip': self.IPVER = ipversion - case other: raise ValueError(f"Wrong IP Address version given: {ipversion}") - - self.IPNEW = ipaddress_new - - # Cloudflare class - self._cloudflare = CloudFlare.CloudFlare(token=__TOKEN) - - with open('cloudflare.json', 'r') as file: - self.dns_replacements = json.load(file) - - def get_zones(self): - # Cloudflare get zones from API token - zones = self._cloudflare.zones.get() - for zone in zones: - self.zone_id = zone['id'] - self.zone_name = zone['name'] - print(f"Zone ID: {zone['id']} | Zone Name: {zone['name']}") - - def get_dns_records(self, log=False): - dns_records_all = self._cloudflare.zones.dns_records.get(self.zone_id) - - dns_records_to_update = {'A' : [], 'AAAA' : []} - for dnsrecord in dns_records_all: - if dnsrecord['type'] in ['A', 'AAAA']: - if log: - print(f"DNS Name: {dnsrecord['name']} | Type : {dnsrecord['type']} | IP: {dnsrecord['content']}") - dns_records_to_update[dnsrecord['type']].append(dnsrecord) - else: - if log: - print(f"| Unknow DNS | Type: {dnsrecord['type']} | Zone: {dnsrecord['name']}") - - self.DNSRECORDS = copy.deepcopy(dns_records_to_update) - - def update_records(self): - if self.IPVER == 'skip': - self.get_dns_records(log=True) - return - - self.get_dns_records() - - ipinformation = { - 'ipv4': [ipaddress.IPv4Address, 'A'], - 'ipv6': [ipaddress.IPv6Address, 'AAAA'] - } - - print(f"Updating IP of version {self.IPVER}") - assert isinstance(ipaddress.ip_address(self.IPNEW), ipinformation[self.IPVER][0]), f'IP {self.IPNEW} is not a valid IPv6 address' - dnstype = ipinformation[self.IPVER][1] - - names_replace = self.dns_replacements[dnstype] - print(names_replace) - for name in names_replace: - if (name == "@"): - fullname = self.HOSTNAME - else: - fullname = f"{name}.{self.HOSTNAME}" - - for record in self.DNSRECORDS[dnstype]: - if record['name'] == fullname: - new_dnsrecord = {"name": fullname, "type": dnstype, "content": self.IPNEW} - print(f"Sending request {new_dnsrecord}") - res = self._cloudflare.zones.dns_records.patch(self.zone_id, record['id'], data=new_dnsrecord) - if res['content'] != new_dnsrecord['content']: - print(res) - - - -if __name__ == '__main__': - dnsupdate = DNSUpdater(sys.argv) - dnsupdate.get_zones() - dnsupdate.update_records() \ No newline at end of file diff --git a/config.ini b/config.ini deleted file mode 100644 index 550c233..0000000 --- a/config.ini +++ /dev/null @@ -1,5 +0,0 @@ -[cloudflare] -TOKEN=w5qP_P47q_G8i55e3OvwuDiuyZXPFU5aXcrO3hnN - -[server] -HOSTNAME=neshura-server.net \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a5c93d4..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cloudflare >= 2.0.0 \ No newline at end of file diff --git a/set_ip.sh b/set_ip.sh deleted file mode 100644 index ea6625d..0000000 --- a/set_ip.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -if [ "$1" = "docker" ]; then - cd "/usr/bin/app" || exit - while : - do - clear - ipv4=$(curl -k -s https://am.i.mullvad.net/ip) - ipv6=$(curl -k -s https://ipv6.am.i.mullvad.net/) - python3 ./cloudflare_script.py 4 "$ipv4" - python3 ./cloudflare_script.py 6 "$ipv6" - sleep 120 - done -else - if [ ! -e cloudflare.json ] || [ ! -e config.ini ]; then - echo "Cloudflare config not found, is the script run in the correct directory?" - else - ipv4=$(curl -k -s https://am.i.mullvad.net/ip -4) - ipv6=$(curl -k -s https://ipv6.am.i.mullvad.net/) - source venv/bin/activate - python3 ./cloudflare_script.py 4 "$ipv4" - python3 ./cloudflare_script.py 6 "$ipv6" - fi -fi \ No newline at end of file From ac2db79f21265b54334a9eda93647c6346b89713 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 01:14:33 +0100 Subject: [PATCH 03/31] Implemented Soft exit during IP-Fetching --- src/main.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index bb16a87..621e1ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use serde_derive::{Deserialize, Serialize}; use reqwest::blocking::get; -use std::fs; +use std::{fs, process::exit}; mod cloudflare; #[derive(Serialize, Deserialize)] @@ -68,7 +68,10 @@ impl Ips { match response { Ok(data) => self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(), - Err(e) => panic!("{:#?}", e) + Err(e) => { + println!("Could not fetch IP4, quitting"); + exit(75); + } } let response = get(ipv6uri); @@ -82,7 +85,10 @@ impl Ips { }; self.ipv6base = stripped; }, - Err(e) => panic!("{:#?}", e) + Err(e) => { + println!("Could not fetch IP6, quitting"); + exit(75); + } } } } From 2433907dd60179fdb4562276a6b0c7372606ea71 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 01:49:02 +0100 Subject: [PATCH 04/31] Seperated API Key into different config file --- .gitignore | 2 +- config.json | 50 ++++++++++++++++++++++++++++ src/main.rs | 95 +++++++++++++++++++++++++++++++++-------------------- 3 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 config.json diff --git a/.gitignore b/.gitignore index 04a070b..060076d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ target/ venv/ .idea/ .vscode/ -config.json +api-key.json diff --git a/config.json b/config.json new file mode 100644 index 0000000..4951766 --- /dev/null +++ b/config.json @@ -0,0 +1,50 @@ +{ + "ipv6_interface": ":da5e:d3ff:feeb:4346", + "zones": [ + { + "email": "neshura@proton.me", + "name": "neshura.net", + "id": "0183f167a051f1e432c0d931478638b5", + "dns_entries": [ + { + "name": "*.neshura.net", + "type4": true, + "type6": true, + "interface": ":da5e:d3ff:feeb:4346" + }, + { + "name": "neshura.net", + "type4": true, + "type6": true, + "interface": ":da5e:d3ff:feeb:4346" + }, + { + "name": "test.neshura.net", + "type4": false, + "type6": true, + "interface": ":da5e:d3ff:feeb:4346" + }, + { + "name": "test2.neshura.net", + "type4": true, + "type6": false, + "interface": ":da5e:d3ff:feeb:4346" + } + ] + }, + { + "email": "neshura@proton.me", + "name": "neshura-server.net", + "id": "146d4cd6a1777376b423aaedc6824818", + "dns_entries": [ + ] + }, + { + "email": "neshura@proton.me", + "name": "neshweb.net", + "id": "75b0d52229357478b734ae0f6d075c15", + "dns_entries": [ + ] + } + ] +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 621e1ce..25a6627 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,18 @@ -use serde_derive::{Deserialize, Serialize}; use reqwest::blocking::get; +use serde_derive::{Deserialize, Serialize}; use std::{fs, process::exit}; mod cloudflare; +fn default_key() -> String { + return "".to_string(); +} + #[derive(Serialize, Deserialize)] struct DnsEntry { name: String, type4: bool, type6: bool, - interface: Option + interface: Option, } #[derive(Serialize, Deserialize)] @@ -16,47 +20,60 @@ struct DnsZone { email: String, name: String, id: String, - dns_entries: Vec + dns_entries: Vec, } #[derive(Serialize, Deserialize)] struct DnsConfig { ipv6_interface: String, + #[serde(default = "default_key")] api_key: String, - zones: Vec + zones: Vec, } impl DnsConfig { fn new() -> DnsConfig { - DnsConfig { - ipv6_interface: "".to_string(), + DnsConfig { + ipv6_interface: "".to_string(), api_key: "".to_string(), - zones: Vec::new() } + zones: Vec::new(), + } } fn load(&mut self) { let file_contents = match fs::read_to_string("config.json") { - Ok(data) => {data}, - Err(e) => panic!("{:#?}", e) + 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) + let config_parse: DnsConfig = match serde_json::from_str(&file_contents) { + Ok(data) => data, + Err(e) => panic!("{:#?}", e), }; + + 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 + ipv6base: String, } impl Ips { fn new() -> Ips { - Ips { - ipv4: "0.0.0.0".to_string(), - ipv6base: ":".to_string() + Ips { + ipv4: "0.0.0.0".to_string(), + ipv6base: ":".to_string(), } } @@ -65,7 +82,7 @@ impl Ips { let ipv6uri = "https://ipv6.am.i.mullvad.net/ip"; let response = get(ipv4uri); - + match response { Ok(data) => self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(), Err(e) => { @@ -80,11 +97,11 @@ impl Ips { Ok(data) => { let ipv6 = data.text().expect(":"); let stripped = match ipv6.strip_suffix(ipv6_interface) { - Some(string) => {string.to_string()}, - None => {":".to_string()} + Some(string) => string.to_string(), + None => ":".to_string(), }; self.ipv6base = stripped; - }, + } Err(e) => { println!("Could not fetch IP6, quitting"); exit(75); @@ -94,10 +111,11 @@ impl Ips { } fn main() { - let mut config = DnsConfig::new(); config.load(); + /* while (true) {} */ + let mut ips = Ips::new(); ips.get(&config.ipv6_interface); @@ -116,38 +134,42 @@ fn main() { agent.load_entries(&zone.email, &zone.id); - for entry in zone.dns_entries { let mut found4 = false; let mut found6 = false; for cloudflare_entry in &agent.dns_entries { - if cloudflare_entry.is_equal(&entry.name) { if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::A && entry.type4 { found4 = true; if cloudflare_entry.is_ip_new(&ips.ipv4) { let success = agent.update_entry(cloudflare_entry, &ips.ipv4); - if success { updated4 += 1 } else { error4 += 1 } - } - else { + if success { + updated4 += 1 + } else { + error4 += 1 + } + } else { unchanged4 += 1; } } - if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::AAAA && entry.type6 { + if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::AAAA && entry.type6 + { found6 = true; let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); if cloudflare_entry.is_ip_new(&ipv6) { let success = agent.update_entry(cloudflare_entry, &ipv6); - if success { updated6 += 1 } else { error6 += 1 } - } - else { + if success { + updated6 += 1 + } else { + error6 += 1 + } + } else { unchanged6 += 1; } } - } //println!("{:#?}", entry.subdomain); // DEBUG @@ -155,16 +177,20 @@ fn main() { if !found4 && entry.type4 { let success = agent.create_entry(&zone.id, "A", &entry.name, &ips.ipv4); - if success { created4 += 1 }; + if success { + created4 += 1 + }; } if !found6 && entry.type6 { let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); let success = agent.create_entry(&zone.id, "AAAA", &entry.name, &ipv6); - if success { created6 += 1 }; + if success { + created6 += 1 + }; } } - + //println!("{:#?}", zone.domain); // DEBUG } println!("++++A Records+++"); @@ -177,5 +203,4 @@ fn main() { println!("{} updated", updated6); println!("{} unchanged", unchanged6); println!("{} errors", error6); - } From f4b7db64c1a7436376a6dcd0d077c0509901fa1f Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 22:44:50 +0100 Subject: [PATCH 05/31] Remove Dockerfile, opting for linux container instead --- Dockerfile | 21 --------------------- api-key-empty.json | 3 +++ 2 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 Dockerfile create mode 100644 api-key-empty.json diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 61f8b38..0000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -# Generate iamge using Python base -FROM python:3.10.8-slim-buster - -WORKDIR /usr/bin/app - -# install required tools -RUN apt update && apt install -y curl - -# Copy only the required files over -COPY set_ip.sh cloudflare_script.py config.ini cloudflare.json requirements.txt ./ - -RUN addgroup --system --gid 1001 pygroup -RUN adduser --system --uid 1001 pyapp - -RUN chown -R pyapp:pygroup /usr/bin/app - -USER pyapp - -RUN pip install -r requirements.txt - -CMD ["bash", "set_ip.sh", "docker"] \ No newline at end of file diff --git a/api-key-empty.json b/api-key-empty.json new file mode 100644 index 0000000..392cfb1 --- /dev/null +++ b/api-key-empty.json @@ -0,0 +1,3 @@ +{ + "api_key": "" +} \ No newline at end of file From b2fdbc823a342d184eec307745a270924abd705a Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:01:17 +0100 Subject: [PATCH 06/31] Remove template file due to new release method --- api-key-empty.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 api-key-empty.json 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 From 0d42b955d2115d9acb67be6960010edf2c3f870c Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:01:27 +0100 Subject: [PATCH 07/31] Add ci-cd file --- .gitlab-ci.yml | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..ed7bb6f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,54 @@ +image: 3.10.8-slim-buster + +.deploy: + rules: + # Regex magic copied from Neshura/page-test, only deploys on x.y.z or higher (x.y) Tags + - if: $CI_COMMIT_TAG =~ /^((([\d])+\.){1,2}[\d]+)\s*$/ && $CI_COMMIT_TAG + +stages: + - build + - release + +## Docker steps + +build: + image: rust:alpine3.17 + stage: build + + variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_BRANCH + CACHING: + + before_script: + - apk add musl-dev openssl-dev + + script: + - echo "Compiling the code..." + - cargo build -r + - echo "Compile complete." + + artifacts: + paths: + - cloudflare-dns-updater + + rules: + - !reference [.deploy, rules] + +Tag Release: + needs: + - job: build + artifacts: true + stage: release + image: registry.gitlab.com/gitlab-org/release-cli:latest + rules: + - !reference [.deploy, rules] + script: + - echo "running release job" + release: + tag_name: '$CI_COMMIT_TAG' + description: '$CI_COMMIT_TAG' + assets: + links: + - name: "cloudflare-dns-updater" + url: "https://gitlab.neshweb.net/Neshura/cloudflare-dns-updater/-/jobs/$JOB_ID/artifacts/download" + \ No newline at end of file From 069e8c7517ea1fe5e5ab5247e29852e262785ddc Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:02:29 +0100 Subject: [PATCH 08/31] Add job execution on manual build --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed7bb6f..da0bc3f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,7 @@ image: 3.10.8-slim-buster rules: # Regex magic copied from Neshura/page-test, only deploys on x.y.z or higher (x.y) Tags - if: $CI_COMMIT_TAG =~ /^((([\d])+\.){1,2}[\d]+)\s*$/ && $CI_COMMIT_TAG + - if: $CI_JOB_MANUAL stages: - build From d831ab90d266d69e1bb5737776bc07699ca44f72 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:04:08 +0100 Subject: [PATCH 09/31] CI/CD Syntax fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index da0bc3f..89c1ae9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ Tag Release: release: tag_name: '$CI_COMMIT_TAG' description: '$CI_COMMIT_TAG' - assets: + assets: links: - name: "cloudflare-dns-updater" url: "https://gitlab.neshweb.net/Neshura/cloudflare-dns-updater/-/jobs/$JOB_ID/artifacts/download" From 9007127c0b8e8119e96d6e45f8fd0710c8daac8c Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:11:53 +0100 Subject: [PATCH 10/31] Remove Job condition, not needed --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 89c1ae9..f42e1a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,6 @@ image: 3.10.8-slim-buster rules: # Regex magic copied from Neshura/page-test, only deploys on x.y.z or higher (x.y) Tags - if: $CI_COMMIT_TAG =~ /^((([\d])+\.){1,2}[\d]+)\s*$/ && $CI_COMMIT_TAG - - if: $CI_JOB_MANUAL stages: - build From 4ac89d11330f3c33f7b6c46c657f4d8de62c2324 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:14:44 +0100 Subject: [PATCH 11/31] Corrected Artifact Path --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f42e1a0..a266a7c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ build: artifacts: paths: - - cloudflare-dns-updater + - /builds/Neshura/cloudflare-dns-updater rules: - !reference [.deploy, rules] From c79f5aba7f94b74b7464fc2ce98e3cbd58d7eb73 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:21:44 +0100 Subject: [PATCH 12/31] Add Job ID --- .gitlab-ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a266a7c..d91ae44 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,9 +27,14 @@ build: - cargo build -r - echo "Compile complete." + after_script: + - echo JOB_ID=&CI_JOB_ID >> job.env + artifacts: paths: - /builds/Neshura/cloudflare-dns-updater + reports: + dotenv: job.env rules: - !reference [.deploy, rules] @@ -43,7 +48,7 @@ Tag Release: rules: - !reference [.deploy, rules] script: - - echo "running release job" + - echo "running Release Job, attaching Artifact from Job $JOB_ID" release: tag_name: '$CI_COMMIT_TAG' description: '$CI_COMMIT_TAG' From 5a22a100ad5b58ffd7a60687219ed6571fd97a8a Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:27:10 +0100 Subject: [PATCH 13/31] Fix JOB_ID Syntax Error --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d91ae44..0a3b0f5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ build: - echo "Compile complete." after_script: - - echo JOB_ID=&CI_JOB_ID >> job.env + - echo JOB_ID=$CI_JOB_ID >> job.env artifacts: paths: From b67b8b05e4e4ce5ee3fd79237107c9fd130008b2 Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:01:17 +0100 Subject: [PATCH 14/31] Revert "Remove template file due to new release method" This reverts commit b2fdbc823a342d184eec307745a270924abd705a. --- api-key-empty.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 api-key-empty.json diff --git a/api-key-empty.json b/api-key-empty.json new file mode 100644 index 0000000..392cfb1 --- /dev/null +++ b/api-key-empty.json @@ -0,0 +1,3 @@ +{ + "api_key": "" +} \ No newline at end of file From 11bf4990bfd39b83e9806cf48e278215ec078bda Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:37:02 +0100 Subject: [PATCH 15/31] Reduced Artifact contents to minimum --- .gitlab-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0a3b0f5..63effe3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,10 +29,14 @@ build: after_script: - echo JOB_ID=$CI_JOB_ID >> job.env + - mkdir /artifacts + - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts + - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts + - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.js /artifacts/api-key.json artifacts: paths: - - /builds/Neshura/cloudflare-dns-updater + - /artifacts reports: dotenv: job.env @@ -54,6 +58,6 @@ Tag Release: description: '$CI_COMMIT_TAG' assets: links: - - name: "cloudflare-dns-updater" + - name: "cloudflare-dns-updater.zip" url: "https://gitlab.neshweb.net/Neshura/cloudflare-dns-updater/-/jobs/$JOB_ID/artifacts/download" \ No newline at end of file From 08c825876dd427fbae6fae459fd6c325cd8172fb Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:43:14 +0100 Subject: [PATCH 16/31] CI/CD debuggign --- .gitlab-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 63effe3..04173be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,10 +29,11 @@ build: after_script: - echo JOB_ID=$CI_JOB_ID >> job.env - - mkdir /artifacts + - mkdir artifacts - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts - - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.js /artifacts/api-key.json + - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json /artifacts/api-key.json + - ls / artifacts: paths: From c20a32a37e5ce470950fc009bd6c63316e4e53ab Mon Sep 17 00:00:00 2001 From: Neshura Date: Sun, 26 Feb 2023 23:56:22 +0100 Subject: [PATCH 17/31] CI/CD debugging --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 04173be..35816b5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,8 +30,8 @@ build: after_script: - echo JOB_ID=$CI_JOB_ID >> job.env - mkdir artifacts - - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts - - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts + - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts/ + - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts/ - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json /artifacts/api-key.json - ls / From c9c9800fff6b5e9ce71684cc2d98b242f7c9cecd Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 00:09:12 +0100 Subject: [PATCH 18/31] CI/CD debugging --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35816b5..932985f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ build: after_script: - echo JOB_ID=$CI_JOB_ID >> job.env - - mkdir artifacts + - mkdir /artifacts - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts/ - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts/ - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json /artifacts/api-key.json From 70557a6bf02f6d96ead922554ef08ac9cae107c8 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 00:19:28 +0100 Subject: [PATCH 19/31] CI/CD debugging --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 932985f..7b309e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,6 +34,8 @@ build: - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts/ - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json /artifacts/api-key.json - ls / + - ls ./ + - ls /builds/Neshura/cloudflare-dns-updater artifacts: paths: From 2a6530b99f316c8d7e175946899ac672df2b756f Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 00:26:27 +0100 Subject: [PATCH 20/31] CI/CD Fix attempt --- .gitlab-ci.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7b309e2..55aae5a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,17 +29,14 @@ build: after_script: - echo JOB_ID=$CI_JOB_ID >> job.env - - mkdir /artifacts - - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater /artifacts/ - - cp /builds/Neshura/cloudflare-dns-updater/config.json /artifacts/ - - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json /artifacts/api-key.json - - ls / - - ls ./ - - ls /builds/Neshura/cloudflare-dns-updater + - mkdir ./artifacts + - cp /builds/Neshura/cloudflare-dns-updater/target/release/cloudflare-dns-updater ./artifacts/ + - cp /builds/Neshura/cloudflare-dns-updater/config.json ./artifacts/ + - cp /builds/Neshura/cloudflare-dns-updater/api-key-empty.json ./artifacts/api-key.json artifacts: paths: - - /artifacts + - ./artifacts reports: dotenv: job.env From 6de678aa1ec9e12ba984c532874133b89e849217 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 00:29:44 +0100 Subject: [PATCH 21/31] CI/CD Syntax Change --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55aae5a..8f39598 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,7 @@ build: artifacts: paths: - - ./artifacts + - ./artifacts/ reports: dotenv: job.env From 3eed4ca15b946c5b9df80f303b44f4799f66bf90 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 01:40:30 +0000 Subject: [PATCH 22/31] Switch from Alpine build environment to Debian Bullseye --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8f39598..70eadb8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ stages: ## Docker steps build: - image: rust:alpine3.17 + image: rust:bullseye stage: build variables: @@ -20,7 +20,7 @@ build: CACHING: before_script: - - apk add musl-dev openssl-dev + - apt install openssl script: - echo "Compiling the code..." From 2e20a00615b59af883e46c735664024c55d5ffea Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 02:04:17 +0000 Subject: [PATCH 23/31] Debian does not need openssl --- .gitlab-ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 70eadb8..7f4ef40 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,9 +19,6 @@ build: IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_BRANCH CACHING: - before_script: - - apt install openssl - script: - echo "Compiling the code..." - cargo build -r From 3bae2f68de7932e4762a1c383e5ff921c9c54a6e Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 03:20:45 +0100 Subject: [PATCH 24/31] Updated IPv6 Parser --- src/main.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 25a6627..3a5d0d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,7 +86,7 @@ impl Ips { match response { Ok(data) => self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(), Err(e) => { - println!("Could not fetch IP4, quitting"); + println!("Could not fetch IPv4, quitting"); exit(75); } } @@ -95,15 +95,18 @@ impl Ips { match response { Ok(data) => { - let ipv6 = data.text().expect(":"); - let stripped = match ipv6.strip_suffix(ipv6_interface) { + 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; } Err(e) => { - println!("Could not fetch IP6, quitting"); + println!("Could not fetch IPv6, quitting"); exit(75); } } From b6dd98f22462b68c50df5db6de41a028fe69562a Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 15:04:12 +0100 Subject: [PATCH 25/31] Added timer based execution and changed change printing --- Cargo.lock | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/main.rs | 98 ++++++++++++++++++++-------- 3 files changed, 252 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51b3541..c34d9cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -44,10 +53,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "cloudflare-dns-updater" version = "0.1.0" dependencies = [ + "chrono", "reqwest", "serde", "serde_derive", @@ -55,6 +80,16 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -71,6 +106,50 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +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", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -278,6 +357,30 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "idna" version = "0.3.0" @@ -340,6 +443,15 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "log" version = "0.4.17" @@ -369,7 +481,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.45.0", ] @@ -391,6 +503,25 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -570,6 +701,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "security-framework" version = "2.8.2" @@ -690,6 +827,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -799,6 +956,12 @@ 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" @@ -826,6 +989,12 @@ dependencies = [ "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" @@ -924,6 +1093,15 @@ 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" diff --git a/Cargo.toml b/Cargo.toml index 8cea999..0b42726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ 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" diff --git a/src/main.rs b/src/main.rs index 3a5d0d9..958a6df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,49 @@ use reqwest::blocking::get; use serde_derive::{Deserialize, Serialize}; -use std::{fs, process::exit}; +use std::{fs, process::exit, thread::{sleep}}; +use chrono::{Utc, Duration, Date}; mod cloudflare; fn default_key() -> String { return "".to_string(); } +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, @@ -113,7 +150,7 @@ impl Ips { } } -fn main() { +fn update_dns() { let mut config = DnsConfig::new(); config.load(); @@ -122,15 +159,9 @@ fn main() { let mut ips = Ips::new(); ips.get(&config.ipv6_interface); - let mut created4: u16 = 0; - let mut updated4: u16 = 0; - let mut unchanged4: u16 = 0; - let mut error4: u16 = 0; + let mut change4 = ChangeTracker::new(); - let mut created6: u16 = 0; - let mut updated6: u16 = 0; - let mut unchanged6: u16 = 0; - let mut error6: u16 = 0; + let mut change6 = ChangeTracker::new(); for zone in config.zones { let mut agent = cloudflare::Instance::new(&config.api_key); @@ -148,12 +179,12 @@ fn main() { if cloudflare_entry.is_ip_new(&ips.ipv4) { let success = agent.update_entry(cloudflare_entry, &ips.ipv4); if success { - updated4 += 1 + change4.updated += 1 } else { - error4 += 1 + change4.error += 1 } } else { - unchanged4 += 1; + change4.unchanged += 1; } } @@ -165,12 +196,12 @@ fn main() { if cloudflare_entry.is_ip_new(&ipv6) { let success = agent.update_entry(cloudflare_entry, &ipv6); if success { - updated6 += 1 + change6.updated += 1 } else { - error6 += 1 + change6.error += 1 } } else { - unchanged6 += 1; + change6.unchanged += 1; } } } @@ -181,7 +212,7 @@ fn main() { if !found4 && entry.type4 { let success = agent.create_entry(&zone.id, "A", &entry.name, &ips.ipv4); if success { - created4 += 1 + change4.created += 1 }; } @@ -189,7 +220,7 @@ fn main() { let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); let success = agent.create_entry(&zone.id, "AAAA", &entry.name, &ipv6); if success { - created6 += 1 + change6.created += 1 }; } } @@ -197,13 +228,28 @@ fn main() { //println!("{:#?}", zone.domain); // DEBUG } println!("++++A Records+++"); - println!("{} created", created4); - println!("{} updated", updated4); - println!("{} unchanged", unchanged4); - println!("{} errors", error4); + change4.print("simple"); println!("++AAAA Records++"); - println!("{} created", created6); - println!("{} updated", updated6); - println!("{} unchanged", unchanged6); - println!("{} errors", error6); + change6.print("simple") +} + +fn main() { + + let mut now = Utc::now() - Duration::seconds(59); + let mut current = now; + + println!("Initial Time is {} {}", now.format("%H:%M:%S"), now.timezone()); + + loop { + now = Utc::now(); + if now >= current + Duration::seconds(60) { + current = now; + + println!("Starting DNS Update at {} {}", now.format("%H:%M:%S"), now.timezone()); + update_dns(); + } + else { + sleep(std::time::Duration::from_millis(500)); + } + } } From fdf2f53e8b3ef1b15ae74a62f790e1d827fe898c Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 15:56:03 +0100 Subject: [PATCH 26/31] Change identifier number for DnsType and make Instance zone_id public --- src/cloudflare/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cloudflare/mod.rs b/src/cloudflare/mod.rs index 1d20413..ab7bdf4 100644 --- a/src/cloudflare/mod.rs +++ b/src/cloudflare/mod.rs @@ -17,9 +17,9 @@ struct CloudflareDnsZone { #[derive(Display, IntoStaticStr)] pub enum CloudflareDnsType { #[strum(serialize = "A")] - A, + A = 4, #[strum(serialize = "AAAA")] - AAAA, + AAAA = 6, TXT, MX } @@ -27,7 +27,7 @@ pub enum CloudflareDnsType { #[derive(Serialize, Deserialize)] pub struct CloudflareDnsEntry { id: String, - zone_id: String, + pub zone_id: String, pub name: String, pub r#type: CloudflareDnsType, content: String, From 4525eb1974d5fcdb46e302aca833858ed6ab2334 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 15:56:24 +0100 Subject: [PATCH 27/31] Split up update function into smaller class functions --- src/main.rs | 192 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 81 deletions(-) diff --git a/src/main.rs b/src/main.rs index 958a6df..9d1257c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,15 @@ +use cloudflare::{Instance, CloudflareDnsType}; use reqwest::blocking::get; use serde_derive::{Deserialize, Serialize}; -use std::{fs, process::exit, thread::{sleep}}; -use chrono::{Utc, Duration, Date}; +use std::{fs, thread::{sleep}}; +use chrono::{Utc, Duration}; mod cloudflare; fn default_key() -> String { return "".to_string(); } +#[derive(Clone)] struct ChangeTracker { created: u16, updated: u16, @@ -52,6 +54,76 @@ struct DnsEntry { interface: Option, } +impl DnsEntry { + fn update(&self, change4: &mut ChangeTracker, change6: &mut ChangeTracker, agent: &Instance, ips: &Ips) { + let mut found4 = false; + let mut found6 = false; + + for cloudflare_entry in &agent.dns_entries { + + // Update IPv4 Entry + if cloudflare_entry.is_equal(&self.name) { + found4 = true; + if cloudflare_entry.r#type == CloudflareDnsType::A && self.type4 { + let success = agent.update_entry(cloudflare_entry, &ips.ipv4); + if success { + change4.updated += 1; + } + else { + change4.error += 1; + } + } + 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) { + let success = agent.update_entry(cloudflare_entry, &ipv6); + if success { + change6.updated += 1 + } + else { + change6.error += 1 + } + } + else { + change6.unchanged += 1; + } + } + // ignore otherwise + else { + + } + + if !found4 && self.type4 { + let success = agent.create_entry(&cloudflare_entry.zone_id, "A", &self.name, &ips.ipv4); + if success { + change4.created += 1; + } + else { + change4.error += 1; + }; + } + + if !found6 && self.type6 { + let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); + let success = agent.create_entry(&cloudflare_entry.zone_id, "AAAA", &self.name, &ipv6); + if success { + change6.created += 1; + } + else { + change6.error += 1; + }; + } + } + } +} + #[derive(Serialize, Deserialize)] struct DnsZone { email: String, @@ -60,6 +132,18 @@ struct DnsZone { dns_entries: Vec, } +impl DnsZone { + fn update(&self, change4: &mut ChangeTracker, change6: &mut ChangeTracker, ips: &Ips, config: &DnsConfig ) { + let mut agent = Instance::new(&config.api_key); + + agent.load_entries(&self.email, &self.id); + + for entry in &self.dns_entries { + entry.update(change4, change6, &agent, &ips); + } + } +} + #[derive(Serialize, Deserialize)] struct DnsConfig { ipv6_interface: String, @@ -114,17 +198,21 @@ impl Ips { } } - fn get(&mut self, ipv6_interface: &str) { + 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; let response = get(ipv4uri); match response { - Ok(data) => self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(), - Err(e) => { - println!("Could not fetch IPv4, quitting"); - exit(75); + Ok(data) => { + self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(); + ret = true; + }, + Err(_e) => { + println!("Could not fetch IPv4"); + ret = false; } } @@ -141,12 +229,15 @@ impl Ips { None => ":".to_string(), }; self.ipv6base = stripped; + ret = true && ret; // Only set ret to true if previous ret is also true } - Err(e) => { - println!("Could not fetch IPv6, quitting"); - exit(75); + Err(_e) => { + println!("Could not fetch IPv6"); + ret = false; } } + + return ret; } } @@ -154,83 +245,22 @@ fn update_dns() { let mut config = DnsConfig::new(); config.load(); - /* while (true) {} */ - let mut ips = Ips::new(); - ips.get(&config.ipv6_interface); + if ips.get(&config.ipv6_interface) { + let mut change4 = ChangeTracker::new(); - let mut change4 = ChangeTracker::new(); + let mut change6 = ChangeTracker::new(); - let mut change6 = ChangeTracker::new(); - - for zone in config.zones { - let mut agent = cloudflare::Instance::new(&config.api_key); - - agent.load_entries(&zone.email, &zone.id); - - for entry in zone.dns_entries { - let mut found4 = false; - let mut found6 = false; - - for cloudflare_entry in &agent.dns_entries { - if cloudflare_entry.is_equal(&entry.name) { - if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::A && entry.type4 { - found4 = true; - if cloudflare_entry.is_ip_new(&ips.ipv4) { - let success = agent.update_entry(cloudflare_entry, &ips.ipv4); - if success { - change4.updated += 1 - } else { - change4.error += 1 - } - } else { - change4.unchanged += 1; - } - } - - if cloudflare_entry.r#type == cloudflare::CloudflareDnsType::AAAA && entry.type6 - { - found6 = true; - let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); - - if cloudflare_entry.is_ip_new(&ipv6) { - let success = agent.update_entry(cloudflare_entry, &ipv6); - if success { - change6.updated += 1 - } else { - change6.error += 1 - } - } else { - change6.unchanged += 1; - } - } - } - - //println!("{:#?}", entry.subdomain); // DEBUG - } - - if !found4 && entry.type4 { - let success = agent.create_entry(&zone.id, "A", &entry.name, &ips.ipv4); - if success { - change4.created += 1 - }; - } - - if !found6 && entry.type6 { - let ipv6 = ips.ipv6base.clone() + entry.interface.as_ref().unwrap(); - let success = agent.create_entry(&zone.id, "AAAA", &entry.name, &ipv6); - if success { - change6.created += 1 - }; - } + // Iterator does not work here + for zone in &config.zones { + zone.update(&mut change4, &mut change6, &ips, &config) } - //println!("{:#?}", zone.domain); // DEBUG + println!("++++A Records+++"); + change4.print("simple"); + println!("++AAAA Records++"); + change6.print("simple") } - println!("++++A Records+++"); - change4.print("simple"); - println!("++AAAA Records++"); - change6.print("simple") } fn main() { From add93f26f46cee133edb8c86f4cfa59686352e06 Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 16:15:00 +0100 Subject: [PATCH 28/31] Fix class methods --- src/main.rs | 103 ++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9d1257c..652dc61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,71 +55,78 @@ struct DnsEntry { } impl DnsEntry { - fn update(&self, change4: &mut ChangeTracker, change6: &mut ChangeTracker, agent: &Instance, ips: &Ips) { + fn update( + &self, + change4: &mut ChangeTracker, + change6: &mut ChangeTracker, + agent: &Instance, + ips: &Ips, + zone: &DnsZone + ) { let mut found4 = false; let mut found6 = false; for cloudflare_entry in &agent.dns_entries { - // Update IPv4 Entry if cloudflare_entry.is_equal(&self.name) { found4 = true; if cloudflare_entry.r#type == CloudflareDnsType::A && self.type4 { let success = agent.update_entry(cloudflare_entry, &ips.ipv4); - if success { - change4.updated += 1; - } - else { - change4.error += 1; - } - } - 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) { - let success = agent.update_entry(cloudflare_entry, &ipv6); - if success { - change6.updated += 1 + if cloudflare_entry.is_ip_new(&ips.ipv4) { + if success { + change4.updated += 1; + } + else { + change4.error += 1; + } } else { - change6.error += 1 + change4.unchanged += 1; } - } - else { - change6.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) { + let success = agent.update_entry(cloudflare_entry, &ipv6); + if success { + change6.updated += 1 + } + else { + change6.error += 1 + } + } + else { + change6.unchanged += 1; + } + } + // ignore otherwise + else {} + } + } + + if !found4 && self.type4 { + let success = agent.create_entry(&zone.id, "A", &self.name, &ips.ipv4); + if success { + change4.created += 1; } - // ignore otherwise else { + change4.error += 1; + }; + } + if !found6 && self.type6 { + let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); + let success = agent.create_entry(&zone.id, "AAAA", &self.name, &ipv6); + if success { + change6.created += 1; } - - if !found4 && self.type4 { - let success = agent.create_entry(&cloudflare_entry.zone_id, "A", &self.name, &ips.ipv4); - if success { - change4.created += 1; - } - else { - change4.error += 1; - }; - } - - if !found6 && self.type6 { - let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); - let success = agent.create_entry(&cloudflare_entry.zone_id, "AAAA", &self.name, &ipv6); - if success { - change6.created += 1; - } - else { - change6.error += 1; - }; - } + else { + change6.error += 1; + }; } } } @@ -139,7 +146,7 @@ impl DnsZone { agent.load_entries(&self.email, &self.id); for entry in &self.dns_entries { - entry.update(change4, change6, &agent, &ips); + entry.update(change4, change6, &agent, &ips, &self); } } } From d9e8bca6491339f51230bf8f3a4027fad68fd7fa Mon Sep 17 00:00:00 2001 From: Neshura Date: Mon, 27 Feb 2023 16:31:29 +0100 Subject: [PATCH 29/31] Removed Test entries from default config.json --- config.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/config.json b/config.json index 4951766..5c59e54 100644 --- a/config.json +++ b/config.json @@ -17,18 +17,6 @@ "type4": true, "type6": true, "interface": ":da5e:d3ff:feeb:4346" - }, - { - "name": "test.neshura.net", - "type4": false, - "type6": true, - "interface": ":da5e:d3ff:feeb:4346" - }, - { - "name": "test2.neshura.net", - "type4": true, - "type6": false, - "interface": ":da5e:d3ff:feeb:4346" } ] }, From f4c9e39b2647e8e2e9c971e29ef11f9b4d86f1f5 Mon Sep 17 00:00:00 2001 From: Neshura Date: Wed, 1 Mar 2023 00:52:09 +0100 Subject: [PATCH 30/31] Clear Screen before printing new DNS Update --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 652dc61..3283381 100644 --- a/src/main.rs +++ b/src/main.rs @@ -275,13 +275,12 @@ fn main() { let mut now = Utc::now() - Duration::seconds(59); let mut current = now; - println!("Initial Time is {} {}", now.format("%H:%M:%S"), now.timezone()); - loop { now = Utc::now(); if now >= current + Duration::seconds(60) { current = now; + print!("\x1B[2J\x1B[1;1H"); println!("Starting DNS Update at {} {}", now.format("%H:%M:%S"), now.timezone()); update_dns(); } From ef94aacc517a90d3752f96ebcbb4ad8d1c7749de Mon Sep 17 00:00:00 2001 From: Neshura Date: Wed, 1 Mar 2023 03:01:09 +0100 Subject: [PATCH 31/31] Added Error handling, switched to using "Result" type for many methods --- src/cloudflare/mod.rs | 149 ++++++++++++++++++++++--------------- src/main.rs | 168 ++++++++++++++++++++++++++++++------------ 2 files changed, 212 insertions(+), 105 deletions(-) diff --git a/src/cloudflare/mod.rs b/src/cloudflare/mod.rs index ab7bdf4..f50acd0 100644 --- a/src/cloudflare/mod.rs +++ b/src/cloudflare/mod.rs @@ -1,6 +1,6 @@ use reqwest::{blocking::{Response, Client}, Url}; use serde_derive::{Deserialize, Serialize}; -use reqwest::header::{HeaderMap, HeaderValue}; +use reqwest::{Error, header::{HeaderMap, HeaderValue}}; use std::{string::String, collections::HashMap}; use strum_macros::{Display, IntoStaticStr}; @@ -64,19 +64,29 @@ impl Instance { /// /// * `email` : E-Mail Address associated with the Zone, required for [`generate_headers()`] /// * `zone_id` : Zone to be used for the Request - pub fn load_entries(&mut self, email: &String, zone_id: &String) { + /// * `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); - let api_response = self.get(&endpoint); - let entries = match api_response.json::() { - Ok(data) => {data}, - Err(e) => panic!("{:#?}", e) - }; + match self.get(&endpoint) { + Ok(response) => { + let entries = match response + .json::() { + Ok(data) => {data}, + Err(e) => panic!("{:#?}", e) + }; + + for entry in entries.result { + self.dns_entries.append(&mut vec!(entry)); + } - for entry in entries.result { - self.dns_entries.append(&mut vec!(entry)); + return Ok(()) + }, + Err(e) => { return Err(e) } } + + } /// Shorthand for Cloudflare API Requests @@ -84,67 +94,77 @@ impl Instance { /// Automatically appends Auth Headers and parses the URL /// /// * `url` : URL for the request, will be parsed to [`Url`] - /// * `email` : E-Mail Address associated with the Zone, required for [`generate_headers()`] - fn get(&self, url: &str) -> Response { + fn get(&self, url: &str) -> Result { let url_parsed = match Url::parse(url) { Ok(url) => {url}, - Err(e) => panic!("{:#?}", e) - }; + Err(e) => panic!("{:#?}", e) // If this call fails the API changed + }; // Continuing without a code update is not possible - let client = Client::new(); - let res = client.get(url_parsed) + let result = match Client::new() + .get(url_parsed) .headers(self.generate_headers()) - .send(); - - let response = match res { - Ok(res) => {res}, - Err(e) => panic!("{:#?}", e) + .send() { + Ok(res) => { Ok(res) }, + Err(e) => { Err(e) } }; - response + return result; } - fn put(&self, url: &str, data: &HashMap<&str, &str>) -> Response { + /// 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) - }; - - let client = Client::new(); - let res = client.put(url_parsed) + 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(); - - let response = match res { - Ok(res) => {res}, - Err(e) => panic!("{:#?}", e) + .send() { + Ok(res) => { Ok(res) }, + Err(e) => { Err(e) } }; - response + return result; } - fn post(&self, url: &str, data: &HashMap<&str, &str>) -> Response { + /// 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) - }; - - let client = Client::new(); - let res = client.post(url_parsed) + 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(); - - let response = match res { - Ok(res) => {res}, - Err(e) => panic!("{:#?}", e) + .send() { + Ok(res) => { Ok(res) }, + Err(e) => { Err(e) } }; - response + return result; } - pub fn update_entry(&self, entry: &CloudflareDnsEntry, new_ip: &String) -> bool { + /// 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(); @@ -153,16 +173,22 @@ impl Instance { json_body.insert("content", new_ip); json_body.insert("ttl", "1"); - let api_response = self.put(&endpoint, &json_body); + match self.put(&endpoint, &json_body) { + Ok(response) => { + let data = response + .json::() + .expect("Response should always be valid JSON"); - let data = api_response.json::().unwrap(); - - let ret = data["success"].as_bool().unwrap(); - - ret + 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) -> bool { + 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(); @@ -171,13 +197,20 @@ impl Instance { json_body.insert("content", ip); json_body.insert("ttl", "1"); - let api_response = self.post(&endpoint, &json_body); + match self.post(&endpoint, &json_body) { + Ok(response) => { + let data = response + .json::() + .expect("Response should always be valid JSON"); - let data = api_response.json::().unwrap(); + let success = data["success"] + .as_bool() + .expect("JSON should always include success bool"); - let ret = data["success"].as_bool().unwrap(); - - ret + return Ok(success) + }, + Err(e) => { return Err(e) }, + } } /// Generate Cloudflare API v4 Auth Headers diff --git a/src/main.rs b/src/main.rs index 3283381..3b7c6af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,31 +55,46 @@ struct DnsEntry { } 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, - agent: &Instance, ips: &Ips, - zone: &DnsZone - ) { + 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 { - // Update IPv4 Entry if cloudflare_entry.is_equal(&self.name) { - found4 = true; + // Update IPv4 Entry if cloudflare_entry.r#type == CloudflareDnsType::A && self.type4 { - let success = agent.update_entry(cloudflare_entry, &ips.ipv4); + found4 = true; if cloudflare_entry.is_ip_new(&ips.ipv4) { - if success { - change4.updated += 1; - } - else { - change4.error += 1; - } + 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; @@ -91,13 +106,17 @@ impl DnsEntry { let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); if cloudflare_entry.is_ip_new(&ipv6) { - let success = agent.update_entry(cloudflare_entry, &ipv6); - if success { - change6.updated += 1 - } - else { - change6.error += 1 - } + 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; @@ -109,25 +128,35 @@ impl DnsEntry { } if !found4 && self.type4 { - let success = agent.create_entry(&zone.id, "A", &self.name, &ips.ipv4); - if success { - change4.created += 1; + 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 } - else { - change4.error += 1; - }; } if !found6 && self.type6 { let ipv6 = ips.ipv6base.clone() + self.interface.as_ref().unwrap(); - let success = agent.create_entry(&zone.id, "AAAA", &self.name, &ipv6); - if success { - change6.created += 1; - } - else { - change6.error += 1; + 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 } } @@ -140,13 +169,40 @@ struct DnsZone { } impl DnsZone { - fn update(&self, change4: &mut ChangeTracker, change6: &mut ChangeTracker, ips: &Ips, config: &DnsConfig ) { - let mut agent = Instance::new(&config.api_key); + /// 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); - agent.load_entries(&self.email, &self.id); + match agent.load_entries(&self.email, &self.id) { + Ok(_) => { + let mut results = Vec::::new(); - for entry in &self.dns_entries { - entry.update(change4, change6, &agent, &ips, &self); + 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 } } } @@ -205,14 +261,16 @@ impl Ips { } } + /// 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; - let response = get(ipv4uri); - - match response { + match get(ipv4uri) { Ok(data) => { self.ipv4 = data.text().expect("0.0.0.0").trim_end().to_owned(); ret = true; @@ -220,12 +278,12 @@ impl Ips { Err(_e) => { println!("Could not fetch IPv4"); ret = false; + + return ret; // Early return since a fail on IPv4 means a likely fail on IPv6 } } - let response = get(ipv6uri); - - match response { + match get(ipv6uri) { Ok(data) => { let ipv6 = match data.text() { Ok(ip) => {ip}, @@ -248,25 +306,41 @@ impl Ips { } } +/// 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 { - zone.update(&mut change4, &mut change6, &ips, &config) + 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") } - println!("++++A Records+++"); - change4.print("simple"); - println!("++AAAA Records++"); - change6.print("simple") } }