From 114659ea21656ede36728fe81b69e65c74a6e9f3 Mon Sep 17 00:00:00 2001 From: FujiApple Date: Thu, 9 Nov 2023 20:12:33 +0800 Subject: [PATCH] feat(tui): add `--tui-privacy-max-ttl` flag to set the maximum ttl of hops which will be masked for privacy (#766) --- src/config.rs | 7 +++++ src/config/cmd.rs | 6 +++- src/config/constants.rs | 3 ++ src/config/file.rs | 1 + src/frontend/config.rs | 4 +++ src/frontend/render/settings.rs | 6 +++- src/frontend/render/table.rs | 55 ++++++++++++++++++++------------- src/frontend/render/world.rs | 24 ++++++++------ src/main.rs | 1 + trippy-config-sample.toml | 2 ++ 10 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/config.rs b/src/config.rs index 389b0ca8..ad23f7a7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -208,6 +208,7 @@ pub struct TrippyConfig { pub tui_max_samples: usize, pub tui_preserve_screen: bool, pub tui_refresh_rate: Duration, + pub tui_privacy_max_ttl: u8, pub tui_address_mode: AddressMode, pub tui_as_mode: AsMode, pub tui_geoip_mode: GeoIpMode, @@ -388,6 +389,11 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig { cfg_file_tui.tui_refresh_rate, String::from(constants::DEFAULT_TUI_REFRESH_RATE), ); + let tui_privacy_max_ttl = cfg_layer( + args.tui_privacy_max_ttl, + cfg_file_tui.tui_privacy_max_ttl, + constants::DEFAULT_TUI_PRIVACY_MAX_TTL, + ); let tui_address_mode = cfg_layer( args.tui_address_mode, cfg_file_tui.tui_address_mode, @@ -550,6 +556,7 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig { tui_max_samples, tui_preserve_screen, tui_refresh_rate, + tui_privacy_max_ttl, tui_address_mode, tui_as_mode, tui_geoip_mode, diff --git a/src/config/cmd.rs b/src/config/cmd.rs index bdddc805..0f2c859b 100644 --- a/src/config/cmd.rs +++ b/src/config/cmd.rs @@ -41,7 +41,7 @@ pub struct Args { #[arg(long, conflicts_with = "protocol", conflicts_with = "udp")] pub tcp: bool, - /// use IPv4 only + /// Use IPv4 only #[arg(short = '4', long, conflicts_with = "ipv6")] pub ipv4: bool, @@ -158,6 +158,10 @@ pub struct Args { #[arg(long)] pub tui_refresh_rate: Option, + /// The maximum ttl of hops which will be masked for privacy [default: 0] + #[arg(long)] + pub tui_privacy_max_ttl: Option, + /// The TUI theme colors [item=color,item=color,..] #[arg(long, value_delimiter(','), value_parser = parse_tui_theme_color_value)] pub tui_theme_colors: Vec<(TuiThemeItem, TuiColor)>, diff --git a/src/config/constants.rs b/src/config/constants.rs index f35ce87a..a8036a1c 100644 --- a/src/config/constants.rs +++ b/src/config/constants.rs @@ -85,6 +85,9 @@ pub const DEFAULT_TUI_ADDRESS_MODE: AddressMode = AddressMode::Host; /// The default value for `tui-refresh-rate`. pub const DEFAULT_TUI_REFRESH_RATE: &str = "100ms"; +/// The default value for `tui_privacy_max_ttl`. +pub const DEFAULT_TUI_PRIVACY_MAX_TTL: u8 = 0; + /// The default value for `dns-resolve-method`. pub const DEFAULT_DNS_RESOLVE_METHOD: DnsResolveMethodConfig = DnsResolveMethodConfig::System; diff --git a/src/config/file.rs b/src/config/file.rs index f0de7927..77661b86 100644 --- a/src/config/file.rs +++ b/src/config/file.rs @@ -135,6 +135,7 @@ pub struct ConfigTui { pub tui_max_samples: Option, pub tui_preserve_screen: Option, pub tui_refresh_rate: Option, + pub tui_privacy_max_ttl: Option, pub tui_address_mode: Option, pub tui_as_mode: Option, pub tui_geoip_mode: Option, diff --git a/src/frontend/config.rs b/src/frontend/config.rs index 274f5eaa..e4e64679 100644 --- a/src/frontend/config.rs +++ b/src/frontend/config.rs @@ -9,6 +9,8 @@ use std::time::Duration; pub struct TuiConfig { /// Refresh rate. pub refresh_rate: Duration, + /// The maximum ttl of hops which will be masked for privacy. + pub privacy_max_ttl: u8, /// Preserve screen on exit. pub preserve_screen: bool, /// How to render addresses. @@ -33,6 +35,7 @@ impl TuiConfig { #[allow(clippy::too_many_arguments)] pub fn new( refresh_rate: Duration, + privacy_ttl: u8, preserve_screen: bool, address_mode: AddressMode, lookup_as_info: bool, @@ -45,6 +48,7 @@ impl TuiConfig { ) -> Self { Self { refresh_rate, + privacy_max_ttl: privacy_ttl, preserve_screen, address_mode, lookup_as_info, diff --git a/src/frontend/render/settings.rs b/src/frontend/render/settings.rs index 6d2e6737..cb133004 100644 --- a/src/frontend/render/settings.rs +++ b/src/frontend/render/settings.rs @@ -157,6 +157,10 @@ fn format_tui_settings(app: &TuiApp) -> Vec { "tui-refresh-rate", format!("{}", format_duration(app.tui_config.refresh_rate)), ), + SettingsItem::new( + "tui-privacy-max-ttl", + format!("{}", app.tui_config.privacy_max_ttl), + ), SettingsItem::new( "tui-address-mode", format_address_mode(app.tui_config.address_mode), @@ -395,7 +399,7 @@ fn format_theme_settings(app: &TuiApp) -> Vec { /// The name and number of items for each tabs in the setting dialog. pub const SETTINGS_TABS: [(&str, usize); 6] = [ - ("Tui", 7), + ("Tui", 8), ("Trace", 14), ("Dns", 4), ("GeoIp", 1), diff --git a/src/frontend/render/table.rs b/src/frontend/render/table.rs index cec0c0ec..898fb6ec 100644 --- a/src/frontend/render/table.rs +++ b/src/frontend/render/table.rs @@ -193,25 +193,33 @@ fn render_hostname( config: &TuiConfig, ) -> (Cell<'static>, u16) { let (hostname, count) = if hop.total_recv() > 0 { - match config.max_addrs { - None => { - let hostnames = hop - .addrs_with_counts() - .map(|(addr, &freq)| format_address(addr, freq, hop, dns, geoip_lookup, config)) - .join("\n"); - let count = hop.addr_count().clamp(1, u8::MAX as usize); - (hostnames, count as u16) - } - Some(max_addr) => { - let hostnames = hop - .addrs_with_counts() - .sorted_unstable_by_key(|(_, &cnt)| cnt) - .rev() - .take(max_addr as usize) - .map(|(addr, &freq)| format_address(addr, freq, hop, dns, geoip_lookup, config)) - .join("\n"); - let count = hop.addr_count().clamp(1, max_addr as usize); - (hostnames, count as u16) + if config.privacy_max_ttl >= hop.ttl() { + (String::from("**Hidden**"), 1) + } else { + match config.max_addrs { + None => { + let hostnames = hop + .addrs_with_counts() + .map(|(addr, &freq)| { + format_address(addr, freq, hop, dns, geoip_lookup, config) + }) + .join("\n"); + let count = hop.addr_count().clamp(1, u8::MAX as usize); + (hostnames, count as u16) + } + Some(max_addr) => { + let hostnames = hop + .addrs_with_counts() + .sorted_unstable_by_key(|(_, &cnt)| cnt) + .rev() + .take(max_addr as usize) + .map(|(addr, &freq)| { + format_address(addr, freq, hop, dns, geoip_lookup, config) + }) + .join("\n"); + let count = hop.addr_count().clamp(1, max_addr as usize); + (hostnames, count as u16) + } } } } else { @@ -334,8 +342,13 @@ fn render_hostname_with_details( config: &TuiConfig, ) -> (Cell<'static>, u16) { let (rendered, count) = if hop.total_recv() > 0 { - let index = app.selected_hop_address; - format_details(hop, index, dns, geoip_lookup, config) + if config.privacy_max_ttl >= hop.ttl() { + let height = if config.lookup_as_info { 6 } else { 3 }; + (String::from("**Hidden**"), height) + } else { + let index = app.selected_hop_address; + format_details(hop, index, dns, geoip_lookup, config) + } } else { let height = if config.lookup_as_info { 6 } else { 3 }; (String::from("No response"), height) diff --git a/src/frontend/render/world.rs b/src/frontend/render/world.rs index 1ddd8eb3..adcd7100 100644 --- a/src/frontend/render/world.rs +++ b/src/frontend/render/world.rs @@ -138,16 +138,20 @@ fn render_map_info_panel(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, entries: & } }) .collect::>(); - let info = match locations.as_slice() { - _ if app.tracer_config().geoip_mmdb_file.is_none() => "GeoIp not enabled".to_string(), - [] if selected_hop.addr_count() > 0 => format!( - "No GeoIp data for hop {} ({})", - selected_hop.ttl(), - selected_hop.addrs().join(", ") - ), - [] => format!("No GeoIp data for hop {}", selected_hop.ttl()), - [loc] => loc.to_string(), - _ => format!("Multiple GeoIp locations for hop {}", selected_hop.ttl()), + let info = if app.tui_config.privacy_max_ttl >= selected_hop.ttl() { + "**Hidden**".to_string() + } else { + match locations.as_slice() { + _ if app.tracer_config().geoip_mmdb_file.is_none() => "GeoIp not enabled".to_string(), + [] if selected_hop.addr_count() > 0 => format!( + "No GeoIp data for hop {} ({})", + selected_hop.ttl(), + selected_hop.addrs().join(", ") + ), + [] => format!("No GeoIp data for hop {}", selected_hop.ttl()), + [loc] => loc.to_string(), + _ => format!("Multiple GeoIp locations for hop {}", selected_hop.ttl()), + } }; let info_panel = Paragraph::new(info) .block( diff --git a/src/main.rs b/src/main.rs index 4adc2c4e..161803a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -298,6 +298,7 @@ fn make_trace_info( fn make_tui_config(args: &TrippyConfig) -> TuiConfig { TuiConfig::new( args.tui_refresh_rate, + args.tui_privacy_max_ttl, args.tui_preserve_screen, args.tui_address_mode, args.dns_lookup_as_info, diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml index 669ed4ec..eaf55fe2 100644 --- a/trippy-config-sample.toml +++ b/trippy-config-sample.toml @@ -250,6 +250,8 @@ tui-preserve-screen = false # The Tui refresh rate [default: 100ms] tui-refresh-rate = "100ms" +# The maximum ttl of hops which will be masked for privacy [default: 1] +tui-privacy-max-ttl = 0 # Tui color theme configure. #