From ddd54e9b20427b716e13d83884b4b0db03953210 Mon Sep 17 00:00:00 2001 From: cgzones Date: Thu, 1 Dec 2022 19:51:04 +0100 Subject: [PATCH] feat(localip): use reserved remote address (#4648) Instead of the remote address of 8.8.8.8 (Google DNS) in the crate local_ipaddress use a reserved IPv4 address, that should never be assigned. Also forward the underlying error on failure. Supersedes: #4614 --- Cargo.lock | 7 ------ Cargo.toml | 1 - src/modules/localip.rs | 57 +++++++++++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bbd4fb90..072f277df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1630,12 +1630,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" -[[package]] -name = "local_ipaddress" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a104730949fbc4c78e4fa98ed769ca0faa02e9818936b61032d2d77526afa9" - [[package]] name = "lock_api" version = "0.4.8" @@ -2770,7 +2764,6 @@ dependencies = [ "guess_host_triple", "home", "indexmap", - "local_ipaddress", "log", "mockall", "nix 0.26.1", diff --git a/Cargo.toml b/Cargo.toml index 9fc98129f..43f432a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ git-features = { version = "0.24.1", optional = true } # default feature restriction addresses https://github.com/starship/starship/issues/4251 git-repository = { version = "0.29.0", default-features = false, features = ["max-performance-safe"] } indexmap = { version = "1.9.2", features = ["serde"] } -local_ipaddress = "0.1.3" log = { version = "0.4.17", features = ["std"] } # nofity-rust is optional (on by default) because the crate doesn't currently build for darwin with nix # see: https://github.com/NixOS/nixpkgs/issues/160876 diff --git a/src/modules/localip.rs b/src/modules/localip.rs index b5385105d..fbdb2c95b 100644 --- a/src/modules/localip.rs +++ b/src/modules/localip.rs @@ -4,14 +4,27 @@ use crate::config::ModuleConfig; use crate::configs::localip::LocalipConfig; use crate::formatter::StringFormatter; +use std::io::Error; +use std::net::UdpSocket; + +fn get_local_ipv4() -> Result { + let socket = UdpSocket::bind("0.0.0.0:0")?; + socket.connect("192.0.2.0:80")?; + + let addr = socket.local_addr()?; + + Ok(addr.ip().to_string()) +} + /// Creates a module with the ipv4 address of the local machine. /// -/// The `local_ipaddress` crate is used to determine the local IP address of your machine. -/// An accurate and fast way, especially if there are multiple IP addresses available, -/// is to connect a UDP socket and then reading its local endpoint. +/// The IP address is gathered from the local endpoint of an UDP socket +/// connected to a reserved IPv4 remote address, which is an accurate and fast +/// way, especially if there are multiple IP addresses available. +/// There should be no actual packets send over the wire. /// /// Will display the ip if all of the following criteria are met: -/// - localip.disabled is false +/// - `localip.disabled` is false /// - `localip.ssh_only` is false OR the user is currently connected as an SSH session (`$SSH_CONNECTION`) pub fn module<'a>(context: &'a Context) -> Option> { let mut module = context.new_module("localip"); @@ -28,11 +41,18 @@ pub fn module<'a>(context: &'a Context) -> Option> { return None; } - let localip = local_ipaddress::get().unwrap_or_default(); - if localip.is_empty() { - log::warn!("unable to determine local ipv4 address"); - return None; - } + let localip = match get_local_ipv4() { + Ok(ip) => ip, + Err(e) => { + // ErrorKind::NetworkUnreachable is unstable + if cfg!(target_os = "linux") && e.raw_os_error() == Some(101) { + "NetworkUnreachable".to_string() + } else { + log::warn!("unable to determine local ipv4 address: {e}"); + return None; + } + } + }; let parsed = StringFormatter::new(config.format).and_then(|formatter| { formatter @@ -60,19 +80,22 @@ pub fn module<'a>(context: &'a Context) -> Option> { #[cfg(test)] mod tests { + use crate::modules::localip::get_local_ipv4; use crate::test::ModuleRenderer; use nu_ansi_term::{Color, Style}; macro_rules! get_localip { () => { - if let Some(localip) = local_ipaddress::get() { - localip - } else { - println!( - "localip was not tested because socket connection failed! \ - This could be caused by an unconventional network setup." - ); - return; + match get_local_ipv4() { + Ok(ip) => ip, + Err(e) => { + println!( + "localip was not tested because socket connection failed! \ + This could be caused by an unconventional network setup. \ + Error: {e}" + ); + return; + } } }; }