Compare commits

...

9 Commits

Author SHA1 Message Date
4f3e689218 gitignore 2024-07-14 16:45:01 +02:00
1e2a739e5b logging 2024-07-14 16:35:07 +02:00
b4aa724e26 logging 2024-07-14 16:32:07 +02:00
74a29878be debug 2024-07-14 16:27:19 +02:00
c74361bb56 cloudflareresponse 2024-07-14 16:24:25 +02:00
bb8a83cb62 simplify 2024-07-14 16:10:13 +02:00
e5a40d5ae7 break out message handler 2024-07-14 16:02:28 +02:00
cd191df5c5 break out massive loop 2024-07-14 15:54:15 +02:00
2d31f61af9 logging and other stuff 2024-07-14 15:40:59 +02:00
9 changed files with 469 additions and 230 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target
logs
dynip-cloudflare.toml

365
Cargo.lock generated
View File

@ -17,15 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -41,61 +32,18 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "atomic-waker"
version = "1.1.2"
@ -161,9 +109,9 @@ checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]]
name = "cc"
version = "1.1.2"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47de7e88bbbd467951ae7f5a6f34f70d1b4d9cfce53d5fd70f74ebe118b3db56"
checksum = "9711f33475c22aab363b05564a17d7b789bf3dfec5ebabb586adee56f0e271b5"
[[package]]
name = "cfg-if"
@ -186,10 +134,14 @@ dependencies = [
]
[[package]]
name = "colorchoice"
version = "1.0.1"
name = "colored"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
dependencies = [
"lazy_static",
"windows-sys 0.48.0",
]
[[package]]
name = "core-foundation"
@ -207,6 +159,23 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "destructure_traitobject"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
[[package]]
name = "dirs"
version = "5.0.1"
@ -234,15 +203,17 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"colored",
"dirs",
"env_logger",
"futures",
"log",
"log4rs",
"netlink-packet-core",
"netlink-packet-route",
"netlink-sys",
"reqwest",
"rtnetlink",
"scopeguard",
"serde",
"serde_json",
"tokio",
@ -258,29 +229,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "env_filter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -389,7 +337,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
@ -638,12 +586,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itoa"
version = "1.0.11"
@ -659,6 +601,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.155"
@ -696,6 +644,43 @@ name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
dependencies = [
"serde",
]
[[package]]
name = "log-mdc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
[[package]]
name = "log4rs"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6"
dependencies = [
"anyhow",
"arc-swap",
"chrono",
"derivative",
"fnv",
"humantime",
"libc",
"log",
"log-mdc",
"once_cell",
"parking_lot",
"rand",
"serde",
"serde-value",
"serde_json",
"serde_yaml",
"thiserror",
"thread-id",
"typemap-ors",
"winapi",
]
[[package]]
name = "memchr"
@ -879,7 +864,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
@ -906,6 +891,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -958,7 +952,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
@ -979,6 +973,12 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.86"
@ -997,6 +997,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.5.2"
@ -1017,35 +1047,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "reqwest"
version = "0.12.5"
@ -1204,9 +1205,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
@ -1217,9 +1218,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
dependencies = [
"core-foundation-sys",
"libc",
@ -1234,6 +1235,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
@ -1242,7 +1253,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
@ -1277,6 +1288,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@ -1323,6 +1347,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[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 = "syn"
version = "2.0.71"
@ -1390,7 +1425,17 @@ checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
name = "thread-id"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b"
dependencies = [
"libc",
"winapi",
]
[[package]]
@ -1435,7 +1480,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
]
[[package]]
@ -1558,6 +1603,15 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typemap-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867"
dependencies = [
"unsafe-any-ors",
]
[[package]]
name = "unicode-bidi"
version = "0.3.15"
@ -1579,6 +1633,21 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unsafe-any-ors"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad"
dependencies = [
"destructure_traitobject",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"
@ -1596,12 +1665,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "vcpkg"
version = "0.2.15"
@ -1644,7 +1707,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
"wasm-bindgen-shared",
]
@ -1678,7 +1741,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.71",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1699,6 +1762,28 @@ dependencies = [
"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-core"
version = "0.52.0"

View File

@ -7,15 +7,21 @@ edition = "2021"
anyhow = "1.0.86"
chrono = "0.4.38"
dirs = "5.0.1"
env_logger = "0.11.3"
futures = "0.3.30"
log = "0.4.22"
netlink-packet-core = "0.7.0"
netlink-packet-route = "0.19.0"
netlink-sys = { version = "0.8.6", features = ["tokio"] }
reqwest = { version = "0.12.5", features = ["json"] }
rtnetlink = "0.14.1" # Updated to a version compatible with netlink-packet-route v0.20.1
rtnetlink = "0.14.1"
serde = { version = "1.0.204", features = ["rc", "derive"] }
serde_json = "1.0.120"
tokio = { version = "1.38.0", features = ["full"] }
toml = "0.8.14"
log4rs = { version = "1.3.0", features = [
"console_appender",
"file_appender",
"rolling_file_appender",
] }
scopeguard = "1.2.0"
colored = "2.1.0"

View File

@ -34,6 +34,14 @@ struct DnsRecord {
meta: HashMap<Box<str>, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug)]
struct CloudflareResponse {
success: bool,
errors: Vec<HashMap<String, serde_json::Value>>,
messages: Vec<HashMap<String, serde_json::Value>>,
result: Option<Vec<DnsRecord>>,
}
impl CloudflareClient {
pub async fn new(api_key: Box<str>, zone_id: Box<str>, domains: Vec<Box<str>>) -> Result<Self> {
let force_ipv4 = IpAddr::from([0, 0, 0, 0]);
@ -57,10 +65,11 @@ impl CloudflareClient {
pub async fn check(&mut self) -> Result<()> {
let new_ip = get_current_public_ipv4(&self.client).await?;
if new_ip == self.current_ip {
info!("IP '{}' is already set", new_ip);
return Ok(());
}
info!(
"Ip has changed from '{}' -> '{}'",
"IP has changed from '{}' -> '{}'",
&self.current_ip, &new_ip
);
self.update_dns_records(new_ip).await?;
@ -75,7 +84,7 @@ impl CloudflareClient {
Ok(r) => r,
Err(e) => {
error!(
"Could not getch dns records for domain '{}': {:?}",
"Could not fetch DNS records for domain '{}': {:?}",
&domain, &e
);
continue;
@ -85,6 +94,10 @@ impl CloudflareClient {
let new_ip_s = new_ip.to_string().into_boxed_str();
for mut record in records.into_iter() {
if record.content == new_ip_s {
info!(
"On {}, ip already updated to '{}'",
&record.name, &record.content
);
continue;
}
info!(
@ -109,7 +122,8 @@ impl CloudflareClient {
"https://api.cloudflare.com/client/v4/zones/{}/dns_records?type=A&name={}",
self.zone_id, domain
);
let mut response = self
let response = self
.client
.get(&url)
.header("Authorization", format!("Bearer {}", self.api_key))
@ -117,12 +131,23 @@ impl CloudflareClient {
.send()
.await?
.error_for_status()?
.json::<HashMap<String, Vec<DnsRecord>>>()
.text()
.await?;
response
.remove("result")
.context("Key result not in return")
let cloudflare_response: CloudflareResponse =
serde_json::from_str(&response).context("Failed to deserialize Cloudflare response")?;
if !cloudflare_response.success {
error!(
"Cloudflare API returned errors: {:?}",
cloudflare_response.errors
);
return Err(anyhow::anyhow!("Cloudflare API returned errors"));
}
cloudflare_response
.result
.context("Key 'result' not found in response")
}
async fn update_dns_record(&self, record: &mut DnsRecord, ip_content: Box<str>) -> Result<()> {

View File

@ -1,11 +1,16 @@
mod cloudflare;
mod config;
mod logging;
mod public_ip;
mod message_handler;
pub mod utils;
pub use cloudflare::CloudflareClient;
pub use config::{get_config_path, read_config, Config};
pub use logging::init_logger;
pub use public_ip::get_current_public_ipv4;
pub use message_handler::MessageHandler;
pub const PROGRAM_NAME: &'static str = "dynip-cloudflare";
pub const MAX_ERORS_IN_ROW_DEFAULT: usize = 10;
const LOG_DIR: &str = "logs";

87
src/logging.rs Normal file
View File

@ -0,0 +1,87 @@
use chrono::Local;
use colored::*;
use log::LevelFilter;
use log4rs::append::console::{ConsoleAppender, Target};
use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller;
use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger;
use log4rs::append::rolling_file::policy::compound::CompoundPolicy;
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use log4rs::encode::{self, Encode};
use log4rs::filter::threshold::ThresholdFilter;
use crate::{LOG_DIR, PROGRAM_NAME};
#[derive(Debug)]
struct ColoredPatternEncoder;
impl ColoredPatternEncoder {
pub fn new() -> Self {
ColoredPatternEncoder {}
}
}
impl Encode for ColoredPatternEncoder {
fn encode(&self, w: &mut dyn encode::Write, record: &log::Record) -> anyhow::Result<()> {
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
let level = match record.level() {
log::Level::Error => record.level().to_string().red().bold(),
log::Level::Warn => record.level().to_string().yellow().bold(),
log::Level::Info => record.level().to_string().green().bold(),
log::Level::Debug => record.level().to_string().blue().bold(),
log::Level::Trace => record.level().to_string().purple().bold(),
};
let message = record.args().to_string().cyan();
Ok(writeln!(w, "{} [{}] {}", now.bold(), level, message)?)
}
}
pub fn init_logger() {
let today = Local::now().format("%Y-%m-%d").to_string();
if std::fs::metadata(LOG_DIR).is_err() {
std::fs::create_dir(LOG_DIR).expect("Failed to create log dir");
}
let log_file_path = format!("{}/{}-{}.log", LOG_DIR, PROGRAM_NAME, today);
let log_file_pattern = format!("{}/{}-{}-{{}}.log", LOG_DIR, PROGRAM_NAME, today);
let level = log::LevelFilter::Info;
let stderr = ConsoleAppender::builder()
.encoder(Box::new(ColoredPatternEncoder::new()))
.target(Target::Stderr)
.build();
const TRIGGER_FILE_SIZE: u64 = 5 * 1024; // 5KB as max log file size to roll
let trigger = SizeTrigger::new(TRIGGER_FILE_SIZE);
let roller = FixedWindowRoller::builder()
.base(0)
.build(&log_file_pattern, 3)
.unwrap();
let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller));
let logfile = log4rs::append::rolling_file::RollingFileAppender::builder()
.encoder(Box::new(PatternEncoder::new(
"{d(%Y-%m-%d %H:%M:%S)} [{l}] {m}\n",
)))
.build(log_file_path, Box::new(policy))
.unwrap();
let config = Config::builder()
.appender(Appender::builder().build("logfile", Box::new(logfile)))
.appender(
Appender::builder()
.filter(Box::new(ThresholdFilter::new(level)))
.build("stderr", Box::new(stderr)),
)
.build(
Root::builder()
.appender("logfile")
.appender("stderr")
.build(LevelFilter::Trace),
)
.unwrap();
log4rs::init_config(config).expect("Failed to initialize logger");
}

View File

@ -1,11 +1,16 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use futures::stream::StreamExt;
use log::{debug, error, info, log_enabled, Level};
use netlink_packet_core::NetlinkPayload;
use netlink_packet_route::RouteNetlinkMessage as RtnlMessage;
use log::{error, info};
use netlink_sys::{AsyncSocket, SocketAddr};
use rtnetlink::new_connection;
use dynip_cloudflare::{utils, CloudflareClient, MAX_ERORS_IN_ROW_DEFAULT};
use dynip_cloudflare::{utils, CloudflareClient, MessageHandler, MAX_ERORS_IN_ROW_DEFAULT};
use scopeguard::defer;
use tokio::{signal, sync::Notify};
const RTNLGRP_LINK: u32 = 1;
const RTNLGRP_IPV4_IFADDR: u32 = 5;
@ -23,7 +28,24 @@ const fn nl_mgrp(group: u32) -> u32 {
#[tokio::main]
async fn main() {
utils::init_logger();
dynip_cloudflare::init_logger();
defer! {
log::logger().flush();
}
let should_exit = Arc::new(AtomicBool::new(false));
let notify = Arc::new(Notify::new());
let should_exit_clone = should_exit.clone();
let notify_clone = notify.clone();
tokio::spawn(async move {
signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler");
should_exit_clone.store(true, Ordering::SeqCst);
notify_clone.notify_one();
});
let config = if let Some(aux) = utils::get_config().await {
aux
} else {
@ -52,64 +74,17 @@ async fn main() {
tokio::spawn(conn);
info!("Listening for IPv4 address changes and interface connect/disconnect events...");
let mut errs_counter: usize = 0;
let errs_max = config.max_errors_in_row.unwrap_or(MAX_ERORS_IN_ROW_DEFAULT);
let mut message_handler = MessageHandler::new(
&mut cloudflare,
config.max_errors_in_row.unwrap_or(MAX_ERORS_IN_ROW_DEFAULT),
);
while let Some((message, _)) = messages.next().await {
match message.payload {
NetlinkPayload::InnerMessage(RtnlMessage::NewAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("New IPv4 address message: {:?}", msg);
} else {
info!("New IPv4 address");
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelAddress(msg)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted IPv4 address message: {:?}", msg);
} else {
info!("Deleted IPv4 address");
}
}
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("New link message (interface connected): {:?}", link);
} else {
info!("New link (interface connected)");
if let Err(e) = cloudflare.check().await {
errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
errs_counter, errs_max, &e
);
if errs_counter >= errs_max {
return;
}
}
}
}
NetlinkPayload::InnerMessage(RtnlMessage::DelLink(link)) => {
if log_enabled!(Level::Debug) {
debug!("Deleted link message (interface disconnected): {:?}", link);
} else {
info!("Deleted link (interface disconnected)");
}
}
_ => {
if log_enabled!(Level::Debug) {
debug!("Unhandled message payload: {:?}", message.payload);
while !should_exit.load(Ordering::SeqCst) {
tokio::select! {
_ = notify.notified() => break,
message = messages.next() => {
if let Some((message, _)) = message {
message_handler.handle_message(message).await;
}
}
}

72
src/message_handler.rs Normal file
View File

@ -0,0 +1,72 @@
use std::fmt;
use log::{debug, error, info};
use netlink_packet_core::{NetlinkMessage, NetlinkPayload};
use netlink_packet_route::RouteNetlinkMessage as RtnlMessage;
use crate::CloudflareClient;
pub struct MessageHandler<'a> {
cloudflare: &'a mut CloudflareClient,
errs_counter: usize,
errs_max: usize,
}
impl<'a> MessageHandler<'a> {
pub fn new(cloudflare: &'a mut CloudflareClient, errs_max: usize) -> Self {
Self {
cloudflare,
errs_counter: 0,
errs_max,
}
}
pub async fn handle_message(&mut self, message: NetlinkMessage<RtnlMessage>) -> Option<()> {
match message.payload {
NetlinkPayload::InnerMessage(RtnlMessage::NewAddress(msg)) => {
self.log_and_check("New IPv4 address", &msg).await
}
NetlinkPayload::InnerMessage(RtnlMessage::DelAddress(msg)) => {
self.log_info("Deleted IPv4 address", &msg).await
}
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)) => {
self.log_and_check("New link (interface connected)", &link)
.await
}
NetlinkPayload::InnerMessage(RtnlMessage::DelLink(link)) => {
self.log_info("Deleted link (interface disconnected)", &link)
.await
}
_ => {
debug!("Unhandled message payload: {:?}", message.payload);
Some(())
}
}
}
async fn log_and_check<D, M>(&mut self, log_msg: &D, msg: &M) -> Option<()>
where
D: fmt::Display + ?Sized,
M: fmt::Debug,
{
info!("{}", log_msg);
debug!("{}: {:?}", log_msg, msg);
if let Err(e) = self.cloudflare.check().await {
self.errs_counter += 1;
error!(
"Failed to check cloudflare ({}/{}): {:?}",
self.errs_counter, self.errs_max, &e
);
if self.errs_counter >= self.errs_max {
return None;
}
}
Some(())
}
async fn log_info<M: fmt::Debug>(&self, log_msg: &str, msg: &M) -> Option<()> {
info!("{}", log_msg);
debug!("{:?} message: {:?}", log_msg, msg);
Some(())
}
}

View File

@ -1,23 +1,5 @@
use env_logger::{Builder, Env};
use log::{error, LevelFilter};
use std::io::Write;
use crate::{get_config_path, read_config, Config};
pub fn init_logger() {
Builder::from_env(Env::default().default_filter_or("info"))
.format(|buf, record| {
writeln!(
buf,
"{} [{}] - {}",
chrono::Local::now().format("%Y:%m:%d %H:%M:%S"),
record.level(),
record.args()
)
})
.filter(None, LevelFilter::Info)
.init();
}
use log::error;
pub async fn get_config() -> Option<Config> {
let config_path = match get_config_path().await {