diff --git a/src/lib.rs b/src/lib.rs index 868e773..564b4d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ mod config; mod exit_listener; mod logging; mod message_handler; +mod network_change_listener; mod public_ip; mod tests; pub mod utils; @@ -14,6 +15,7 @@ pub use config::{get_config_path, read_config, Config}; pub use exit_listener::ExitListener; pub use logging::init_logger; pub use message_handler::MessageHandler; +pub use network_change_listener::NetworkChangeListener; pub use public_ip::get_current_public_ipv4; pub const PROGRAM_NAME: &'static str = "dynip-cloudflare"; diff --git a/src/main.rs b/src/main.rs index 4f3d106..826b695 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,14 @@ // SPDX: BSD-2-Clause use futures::future::{self, Either}; -use futures::stream::StreamExt; use log::{error, info}; -use netlink_sys::{AsyncSocket, SocketAddr}; -use rtnetlink::new_connection; - -use dynip_cloudflare::{ - utils::{self, duration_to_string}, - CloudflareClient, ExitListener, MessageHandler, MAX_ERORS_IN_ROW_DEFAULT, -}; use scopeguard::defer; use tokio::time; -const RTNLGRP_LINK: u32 = 1; -const RTNLGRP_IPV4_IFADDR: u32 = 5; - -const fn nl_mgrp(group: u32) -> u32 { - if group > 31 { - panic!("use netlink_sys::Socket::add_membership() for this group"); - } - if group == 0 { - 0 - } else { - 1 << (group - 1) - } -} +use dynip_cloudflare::{ + utils, CloudflareClient, ExitListener, MessageHandler, NetworkChangeListener, + MAX_ERORS_IN_ROW_DEFAULT, +}; #[tokio::main] async fn main() { @@ -50,18 +33,16 @@ async fn main() { } }; - let (mut conn, mut _handle, mut messages) = new_connection().unwrap(); - let groups = nl_mgrp(RTNLGRP_LINK) | nl_mgrp(RTNLGRP_IPV4_IFADDR); - - let addr = SocketAddr::new(0, groups); - - if let Err(e) = conn.socket_mut().socket_mut().bind(&addr) { - error!("Failed to bind to socket: {:?}", &e); - return; - } - - tokio::spawn(conn); - info!("Listening for IPv4 address changes and interface connect/disconnect events..."); + let mut network_change_listener = match NetworkChangeListener::new() { + Some(aux) => { + info!("Listening for IPv4 address changes and interface connect/disconnect events..."); + aux + } + None => { + error!("Failed to initialize networkchangelistener"); + return; + } + }; let mut message_handler = MessageHandler::new( &mut cloudflare, @@ -84,12 +65,12 @@ async fn main() { _ = exit_listener.notified() => break, _ = tick_future => { if let Some(duration) = config.max_duration.as_ref() { - let duration_string = duration_to_string(duration); + let duration_string = utils::duration_to_string(duration); let log_string = format!("{} has passed since last check, checking...", duration_string.trim()); message_handler.log_and_check(Some(&log_string), Option::<&&str>::None).await; } } - message = messages.next() => { + message = network_change_listener.next_message() => { if let Some((message, _)) = message { if let Some(interval) = interval.as_mut() { interval.reset(); diff --git a/src/network_change_listener.rs b/src/network_change_listener.rs new file mode 100644 index 0000000..e425609 --- /dev/null +++ b/src/network_change_listener.rs @@ -0,0 +1,60 @@ +use futures::{ + channel::mpsc::UnboundedReceiver, + stream::{Next, StreamExt}, +}; +use log::error; +use netlink_packet_core::NetlinkMessage; +use netlink_packet_route::RouteNetlinkMessage; +use netlink_sys::{AsyncSocket, SocketAddr}; +use rtnetlink::new_connection; +use tokio::task::JoinHandle; + +const RTNLGRP_LINK: u32 = 1; +const RTNLGRP_IPV4_IFADDR: u32 = 5; + +const fn nl_mgrp(group: u32) -> u32 { + if group > 31 { + panic!("use netlink_sys::Socket::add_membership() for this group"); + } + if group == 0 { + 0 + } else { + 1 << (group - 1) + } +} + +type Messages = UnboundedReceiver<(NetlinkMessage, SocketAddr)>; +pub struct NetworkChangeListener { + messages: Messages, + thread_handle: JoinHandle<()>, +} + +impl NetworkChangeListener { + pub fn new() -> Option { + let (mut conn, mut _handle, messages) = new_connection().ok()?; + let groups = nl_mgrp(RTNLGRP_LINK) | nl_mgrp(RTNLGRP_IPV4_IFADDR); + + let addr = SocketAddr::new(0, groups); + + if let Err(e) = conn.socket_mut().socket_mut().bind(&addr) { + error!("Failed to bind to socket: {:?}", &e); + return None; + } + + let thread_handle = tokio::spawn(conn); + Some(Self { + messages, + thread_handle, + }) + } + + pub fn next_message(&mut self) -> Next<'_, Messages> { + self.messages.next() + } +} + +impl Drop for NetworkChangeListener { + fn drop(&mut self) { + self.thread_handle.abort(); + } +}