Skip to content

Commit

Permalink
feat: add dns_resolve_all flag to trace to all resolved ips (#743)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Nov 5, 2023
1 parent cd369aa commit e7b0f4b
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 33 deletions.
29 changes: 21 additions & 8 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ pub struct TrippyConfig {
pub tui_bindings: TuiBindings,
pub mode: Mode,
pub privilege_mode: PrivilegeMode,
pub dns_resolve_all: bool,
pub report_cycles: usize,
pub geoip_mmdb_file: Option<String>,
pub max_rounds: Option<usize>,
Expand Down Expand Up @@ -277,13 +278,16 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
cfg_file_trace.unprivileged,
constants::DEFAULT_UNPRIVILEGED,
);

let privilege_mode = if unprivileged {
PrivilegeMode::Unprivileged
} else {
PrivilegeMode::Privileged
};

let dns_resolve_all = cfg_layer_bool_flag(
args.dns_resolve_all,
cfg_file_dns.dns_resolve_all,
constants::DEFAULT_DNS_RESOLVE_ALL,
);
let verbose = args.verbose;
let log_format = cfg_layer(
args.log_format,
Expand Down Expand Up @@ -499,7 +503,7 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
validate_privilege(privilege_mode, has_privileges, needs_privileges)?;
validate_logging(mode, verbose)?;
validate_strategy(multipath_strategy, unprivileged)?;
validate_multi(mode, protocol, &args.targets)?;
validate_multi(mode, protocol, &args.targets, dns_resolve_all)?;
validate_ttl(first_ttl, max_ttl)?;
validate_max_inflight(max_inflight)?;
validate_read_timeout(read_timeout)?;
Expand Down Expand Up @@ -554,6 +558,7 @@ impl TryFrom<(Args, &Platform)> for TrippyConfig {
tui_bindings,
mode,
privilege_mode,
dns_resolve_all,
report_cycles,
geoip_mmdb_file,
max_rounds,
Expand Down Expand Up @@ -640,18 +645,26 @@ fn validate_strategy(strategy: MultipathStrategy, unprivileged: bool) -> anyhow:
}

/// We only allow multiple targets to be specified for the Tui and for `Icmp` tracing.
fn validate_multi(mode: Mode, protocol: TracerProtocol, targets: &[String]) -> anyhow::Result<()> {
fn validate_multi(
mode: Mode,
protocol: TracerProtocol,
targets: &[String],
all_resolved_ips: bool,
) -> anyhow::Result<()> {
// TODO validate dns_resolve_all
match (mode, protocol) {
(Mode::Stream | Mode::Pretty | Mode::Markdown | Mode::Csv | Mode::Json, _)
if targets.len() > 1 =>
if targets.len() > 1 || all_resolved_ips =>
{
Err(anyhow!(
"only a single target may be specified for this mode"
))
}
(_, TracerProtocol::Tcp | TracerProtocol::Udp) if targets.len() > 1 => Err(anyhow!(
"only a single target may be specified for TCP and UDP tracing"
)),
(_, TracerProtocol::Tcp | TracerProtocol::Udp) if targets.len() > 1 || all_resolved_ips => {
Err(anyhow!(
"only a single target may be specified for TCP and UDP tracing"
))
}
_ => Ok(()),
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/config/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ pub struct Args {
#[arg(value_enum, short = 'r', long)]
pub dns_resolve_method: Option<DnsResolveMethodConfig>,

/// Trace to all IPs resolved from DNS lookup [default: false]
#[arg(short = 'y', long)]
pub dns_resolve_all: bool,

/// The maximum time to wait to perform DNS queries [default: 5s]
#[arg(long)]
pub dns_timeout: Option<String>,
Expand Down
3 changes: 3 additions & 0 deletions src/config/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub const DEFAULT_MODE: Mode = Mode::Tui;
/// The default value for `unprivileged`.
pub const DEFAULT_UNPRIVILEGED: bool = false;

/// The default value for `all_resolved_ips`.
pub const DEFAULT_DNS_RESOLVE_ALL: bool = false;

/// The default value for `log-format`.
pub const DEFAULT_LOG_FORMAT: LogFormat = LogFormat::Pretty;

Expand Down
1 change: 1 addition & 0 deletions src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub struct ConfigStrategy {
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct ConfigDns {
pub dns_resolve_method: Option<DnsResolveMethodConfig>,
pub dns_resolve_all: Option<bool>,
pub dns_lookup_as_info: Option<bool>,
pub dns_timeout: Option<String>,
}
Expand Down
6 changes: 5 additions & 1 deletion src/frontend/render/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ fn format_dns_settings(app: &TuiApp) -> Vec<SettingsItem> {
"dns-resolve-method",
format_dns_method(app.resolver.config().resolve_method),
),
SettingsItem::new(
"dns-resolve-all",
format!("{}", app.tracer_config().dns_resolve_all),
),
SettingsItem::new(
"dns-lookup-as-info",
format!("{}", app.tui_config.lookup_as_info),
Expand Down Expand Up @@ -393,7 +397,7 @@ fn format_theme_settings(app: &TuiApp) -> Vec<SettingsItem> {
pub const SETTINGS_TABS: [(&str, usize); 6] = [
("Tui", 7),
("Trace", 14),
("Dns", 3),
("Dns", 4),
("GeoIp", 1),
("Bindings", 26),
("Theme", 27),
Expand Down
79 changes: 55 additions & 24 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,8 @@ fn main() -> anyhow::Result<()> {
let _guard = configure_logging(&cfg);
let resolver = start_dns_resolver(&cfg)?;
let geoip_lookup = create_geoip_lookup(&cfg)?;
let traces: Vec<_> = cfg
.targets
.iter()
.enumerate()
.map(|(i, target_host)| start_tracer(&cfg, target_host, platform.pid + i as u16, &resolver))
.collect::<anyhow::Result<Vec<_>>>()?;
let addrs = resolve_targets(&cfg, &resolver)?;
let traces = start_tracers(&cfg, &addrs, platform.pid)?;
Platform::drop_privileges()?;
run_frontend(&cfg, resolver, geoip_lookup, traces)?;
Ok(())
Expand Down Expand Up @@ -125,30 +121,54 @@ fn configure_logging(cfg: &TrippyConfig) -> Option<FlushGuard> {
None
}

/// Resolve targets.
fn resolve_targets(cfg: &TrippyConfig, resolver: &DnsResolver) -> anyhow::Result<Vec<TargetInfo>> {
cfg.targets
.iter()
.flat_map(|target| match resolver.lookup(target) {
Ok(addrs) => addrs
.into_iter()
.enumerate()
.take_while(|(i, _)| if cfg.dns_resolve_all { true } else { *i == 0 })
.map(|(i, addr)| {
let hostname = if cfg.dns_resolve_all {
format!("{} [{}]", target, i + 1)
} else {
target.to_string()
};
Ok(TargetInfo { hostname, addr })
})
.collect::<Vec<_>>()
.into_iter(),
Err(e) => {
vec![Err(anyhow!("failed to resolve target: {} ({})", target, e))].into_iter()
}
})
.collect::<anyhow::Result<Vec<_>>>()
}

/// Start all tracers.
fn start_tracers(
cfg: &TrippyConfig,
addrs: &[TargetInfo],
pid: u16,
) -> anyhow::Result<Vec<TraceInfo>> {
addrs
.iter()
.enumerate()
.map(|(i, TargetInfo { hostname, addr })| {
start_tracer(cfg, hostname, *addr, pid + i as u16)
})
.collect::<anyhow::Result<Vec<_>>>()
}

/// Start a tracer to a given target.
fn start_tracer(
cfg: &TrippyConfig,
target_host: &str,
target_addr: IpAddr,
trace_identifier: u16,
resolver: &DnsResolver,
) -> Result<TraceInfo, Error> {
let target_addr: IpAddr = resolver
.lookup(target_host)
.map_err(|e| anyhow!("failed to resolve target: {} ({})", target_host, e))?
.into_iter()
.find(|addr| {
matches!(
(cfg.addr_family, addr),
(TracerAddrFamily::Ipv4, IpAddr::V4(_)) | (TracerAddrFamily::Ipv6, IpAddr::V6(_))
)
})
.ok_or_else(|| {
anyhow!(
"failed to find an {:?} address for target: {}",
cfg.addr_family,
target_host
)
})?;
let source_addr = match cfg.source_addr {
None => SourceAddr::discover(target_addr, cfg.port_direction, cfg.interface.as_deref())?,
Some(addr) => SourceAddr::validate(addr)?,
Expand Down Expand Up @@ -270,6 +290,7 @@ fn make_trace_info(
args.payload_pattern,
args.interface.clone(),
args.geoip_mmdb_file.clone(),
args.dns_resolve_all,
)
}

Expand All @@ -289,6 +310,13 @@ fn make_tui_config(args: &TrippyConfig) -> TuiConfig {
)
}

/// Information about a tracing target.
#[derive(Debug, Clone)]
pub struct TargetInfo {
pub hostname: String,
pub addr: IpAddr,
}

/// Information about a `Trace` needed for the Tui, stream and reports.
#[derive(Debug, Clone)]
pub struct TraceInfo {
Expand All @@ -313,6 +341,7 @@ pub struct TraceInfo {
pub payload_pattern: u8,
pub interface: Option<String>,
pub geoip_mmdb_file: Option<String>,
pub dns_resolve_all: bool,
}

impl TraceInfo {
Expand Down Expand Up @@ -340,6 +369,7 @@ impl TraceInfo {
payload_pattern: u8,
interface: Option<String>,
geoip_mmdb_file: Option<String>,
dns_resolve_all: bool,
) -> Self {
Self {
data,
Expand All @@ -363,6 +393,7 @@ impl TraceInfo {
payload_pattern,
interface,
geoip_mmdb_file,
dns_resolve_all,
}
}
}
6 changes: 6 additions & 0 deletions trippy-config-sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ read-timeout = "10ms"
# cloudflare - Resolve using the Cloudflare `1.1.1.1` DNS service
dns-resolve-method = "system"

# Trace to all IPs resolved from DNS lookup (ICMP only) [default: false]
#
# When set to true a trace will be started for all IPs resolved for all given targets.
# When set to false a trace will be started for one arbitrarily chosen IP per given target.
dns-resolve-all = false

# Whether to lookup AS information [default: false]
#
# If enabled, AS (autonomous system) information is retrieved during DNS
Expand Down

0 comments on commit e7b0f4b

Please sign in to comment.