Skip to content

Commit

Permalink
feat: add support dns-resolve-family (#864)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Jan 21, 2024
1 parent 7c3da55 commit e5cce29
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 136 deletions.
104 changes: 77 additions & 27 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ use std::collections::HashMap;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use trippy::dns::ResolveMethod;
use trippy::dns::{IpAddrFamily, ResolveMethod};
use trippy::tracing::{
defaults, AddrFamily, IcmpExtensionParseMode, MultipathStrategy, PortDirection, PrivilegeMode,
Protocol,
defaults, IcmpExtensionParseMode, MultipathStrategy, PortDirection, PrivilegeMode, Protocol,
};

mod binding;
Expand Down Expand Up @@ -76,20 +75,28 @@ impl From<Protocol> for ProtocolConfig {
}

/// The address family.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, ValueEnum, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AddressFamilyConfig {
/// Internet Protocol V4
/// Ipv4 only.
Ipv4,
/// Internet Protocol V6
/// Ipv6 only.
Ipv6,
/// Ipv6 with a fallback to Ipv4
#[serde(rename = "ipv6-then-ipv4")]
Ipv6ThenIpv4,
/// Ipv4 with a fallback to Ipv6
#[serde(rename = "ipv4-then-ipv6")]
Ipv4ThenIpv6,
}

impl From<AddrFamily> for AddressFamilyConfig {
fn from(value: AddrFamily) -> Self {
impl From<IpAddrFamily> for AddressFamilyConfig {
fn from(value: IpAddrFamily) -> Self {
match value {
AddrFamily::Ipv4 => Self::Ipv4,
AddrFamily::Ipv6 => Self::Ipv6,
IpAddrFamily::Ipv4Only => Self::Ipv4,
IpAddrFamily::Ipv6Only => Self::Ipv6,
IpAddrFamily::Ipv6thenIpv4 => Self::Ipv6ThenIpv4,
IpAddrFamily::Ipv4thenIpv6 => Self::Ipv4ThenIpv6,
}
}
}
Expand Down Expand Up @@ -270,7 +277,7 @@ impl TrippyAction {
pub struct TrippyConfig {
pub targets: Vec<String>,
pub protocol: Protocol,
pub addr_family: AddrFamily,
pub addr_family: IpAddrFamily,
pub first_ttl: u8,
pub max_ttl: u8,
pub min_round_duration: Duration,
Expand Down Expand Up @@ -378,6 +385,11 @@ impl TrippyConfig {
cfg_file_strategy.protocol,
ProtocolConfig::from(defaults::DEFAULT_STRATEGY_PROTOCOL),
);
let addr_family_cfg = cfg_layer(
args.addr_family,
cfg_file_strategy.addr_family,
constants::DEFAULT_ADDR_FAMILY,
);
let target_port = cfg_layer_opt(args.target_port, cfg_file_strategy.target_port);
let source_port = cfg_layer_opt(args.source_port, cfg_file_strategy.source_port);
let source_address = cfg_layer_opt(args.source_address, cfg_file_strategy.source_address);
Expand Down Expand Up @@ -541,19 +553,42 @@ impl TrippyConfig {
.map_err(|_| anyhow!("invalid source IP address format: {}", addr))
})
.transpose()?;
let addr_family = match (args.ipv4, args.ipv6, cfg_file_strategy.addr_family) {
(false, false, None) => defaults::DEFAULT_ADDRESS_FAMILY,
(false, false, Some(AddressFamilyConfig::Ipv4)) | (true, _, _) => AddrFamily::Ipv4,
(false, false, Some(AddressFamilyConfig::Ipv6)) | (_, true, _) => AddrFamily::Ipv6,

#[allow(clippy::match_same_arms)]
let addr_family = match (
args.ipv4,
args.ipv6,
addr_family_cfg,
multipath_strategy_cfg,
) {
(false, false, AddressFamilyConfig::Ipv4, _) => IpAddrFamily::Ipv4Only,
(false, false, AddressFamilyConfig::Ipv6, _) => IpAddrFamily::Ipv6Only,
// we "downgrade" to `Ipv4Only` for `Dublin` rather than fail.
(false, false, AddressFamilyConfig::Ipv4ThenIpv6, MultipathStrategyConfig::Dublin) => {
IpAddrFamily::Ipv4Only
}
(false, false, AddressFamilyConfig::Ipv6ThenIpv4, MultipathStrategyConfig::Dublin) => {
IpAddrFamily::Ipv4Only
}
(false, false, AddressFamilyConfig::Ipv4ThenIpv6, _) => IpAddrFamily::Ipv4thenIpv6,
(false, false, AddressFamilyConfig::Ipv6ThenIpv4, _) => IpAddrFamily::Ipv6thenIpv4,
(true, _, _, _) => IpAddrFamily::Ipv4Only,
(_, true, _, _) => IpAddrFamily::Ipv6Only,
};

#[allow(clippy::match_same_arms)]
let multipath_strategy = match (multipath_strategy_cfg, addr_family) {
(MultipathStrategyConfig::Classic, _) => Ok(MultipathStrategy::Classic),
(MultipathStrategyConfig::Paris, _) => Ok(MultipathStrategy::Paris),
(MultipathStrategyConfig::Dublin, AddrFamily::Ipv4) => Ok(MultipathStrategy::Dublin),
(MultipathStrategyConfig::Dublin, AddrFamily::Ipv6) => Err(anyhow!(
(
MultipathStrategyConfig::Dublin,
IpAddrFamily::Ipv4Only | IpAddrFamily::Ipv4thenIpv6 | IpAddrFamily::Ipv6thenIpv4,
) => Ok(MultipathStrategy::Dublin),
(MultipathStrategyConfig::Dublin, IpAddrFamily::Ipv6Only) => Err(anyhow!(
"Dublin multipath strategy not implemented for IPv6 yet!"
)),
}?;

let port_direction = match (protocol, source_port, target_port, multipath_strategy_cfg) {
(Protocol::Icmp, _, _, _) => PortDirection::None,
(Protocol::Udp, None, None, _) => PortDirection::new_fixed_src(pid.max(1024)),
Expand Down Expand Up @@ -683,7 +718,7 @@ impl Default for TrippyConfig {
Self {
targets: vec![],
protocol: defaults::DEFAULT_STRATEGY_PROTOCOL,
addr_family: defaults::DEFAULT_ADDRESS_FAMILY,
addr_family: dns_resolve_family(constants::DEFAULT_ADDR_FAMILY),
first_ttl: defaults::DEFAULT_STRATEGY_FIRST_TTL,
max_ttl: defaults::DEFAULT_STRATEGY_MAX_TTL,
min_round_duration: defaults::DEFAULT_STRATEGY_MIN_ROUND_DURATION,
Expand Down Expand Up @@ -743,6 +778,15 @@ fn dns_resolve_method(dns_resolve_method: DnsResolveMethodConfig) -> ResolveMeth
}
}

fn dns_resolve_family(dns_resolve_family: AddressFamilyConfig) -> IpAddrFamily {
match dns_resolve_family {
AddressFamilyConfig::Ipv4 => IpAddrFamily::Ipv4Only,
AddressFamilyConfig::Ipv6 => IpAddrFamily::Ipv6Only,
AddressFamilyConfig::Ipv6ThenIpv4 => IpAddrFamily::Ipv6thenIpv4,
AddressFamilyConfig::Ipv4ThenIpv6 => IpAddrFamily::Ipv4thenIpv6,
}
}

fn cfg_layer<T>(fst: Option<T>, snd: Option<T>, def: T) -> T {
match (fst, snd) {
(Some(val), _) | (None, Some(val)) => val,
Expand Down Expand Up @@ -1093,7 +1137,7 @@ mod tests {
#[test_case("trip example.com", Ok(cfg().multipath_strategy(MultipathStrategy::Classic).build()); "default strategy")]
#[test_case("trip example.com --multipath-strategy classic", Ok(cfg().multipath_strategy(MultipathStrategy::Classic).build()); "classic strategy")]
#[test_case("trip example.com --multipath-strategy paris --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Paris).protocol(Protocol::Udp).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "paris strategy")]
#[test_case("trip example.com --multipath-strategy dublin --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Dublin).protocol(Protocol::Udp).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "dublin strategy")]
#[test_case("trip example.com --multipath-strategy dublin --udp", Ok(cfg().multipath_strategy(MultipathStrategy::Dublin).protocol(Protocol::Udp).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "dublin strategy")]
#[test_case("trip example.com --multipath-strategy tokyo", Err(anyhow!("error: one of the values isn't valid for an argument")); "invalid strategy")]
#[test_case("trip example.com", Ok(cfg().protocol(Protocol::Icmp).port_direction(PortDirection::None).build()); "default protocol")]
#[test_case("trip example.com --protocol icmp", Ok(cfg().protocol(Protocol::Icmp).port_direction(PortDirection::None).build()); "icmp protocol")]
Expand All @@ -1116,18 +1160,24 @@ mod tests {
#[test_case("trip example.com --udp --multipath-strategy paris --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol paris strategy custom src port")]
#[test_case("trip example.com --udp --multipath-strategy paris --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol paris strategy custom target port")]
#[test_case("trip example.com --udp --multipath-strategy paris --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Paris).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol paris strategy custom both ports")]
#[test_case("trip example.com --udp --multipath-strategy dublin", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "udp protocol dublin strategy default ports")]
#[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol dublin strategy custom src port")]
#[test_case("trip example.com --udp --multipath-strategy dublin --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol dublin strategy custom target port")]
#[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol dublin strategy custom both ports")]
#[test_case("trip example.com --udp --multipath-strategy dublin", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(1024))).build()); "udp protocol dublin strategy default ports")]
#[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedSrc(Port(33000))).build()); "udp protocol dublin strategy custom src port")]
#[test_case("trip example.com --udp --multipath-strategy dublin --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedDest(Port(5000))).build()); "udp protocol dublin strategy custom target port")]
#[test_case("trip example.com --udp --multipath-strategy dublin --source-port 33000 --target-port 5000", Ok(cfg().protocol(Protocol::Udp).multipath_strategy(MultipathStrategy::Dublin).addr_family(IpAddrFamily::Ipv4Only).port_direction(PortDirection::FixedBoth(Port(33000), Port(5000))).build()); "udp protocol dublin strategy custom both ports")]
#[test_case("trip example.com --icmp --multipath-strategy paris", Err(anyhow!("Paris multipath strategy not support for icmp")); "paris with invalid protocol icmp")]
#[test_case("trip example.com --icmp --multipath-strategy dublin", Err(anyhow!("Dublin multipath strategy not support for icmp")); "dublin with invalid protocol icmp")]
#[test_case("trip example.com --tcp --multipath-strategy paris", Err(anyhow!("Paris multipath strategy not yet supported for tcp")); "paris with invalid protocol tcp")]
#[test_case("trip example.com --tcp --multipath-strategy dublin", Err(anyhow!("Dublin multipath strategy not yet supported for tcp")); "dublin with invalid protocol tcp")]
#[test_case("trip example.com --udp --source-port 33000 --target-port 5000", Err(anyhow!("only one of source-port and target-port may be fixed (except IPv4/udp protocol with dublin or paris strategy)")); "udp protocol custom both ports with invalid strategy")]
#[test_case("trip example.com", Ok(cfg().addr_family(AddrFamily::Ipv4).build()); "default address family shortcut")]
#[test_case("trip example.com -4", Ok(cfg().addr_family(AddrFamily::Ipv4).build()); "ipv4 address family shortcut")]
#[test_case("trip example.com -6", Ok(cfg().addr_family(AddrFamily::Ipv6).build()); "ipv6 address family shortcut")]
#[test_case("trip example.com", Ok(cfg().addr_family(IpAddrFamily::Ipv4thenIpv6).build()); "default address family")]
#[test_case("trip example.com --addr-family ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "ipv4 address family")]
#[test_case("trip example.com --addr-family ipv6", Ok(cfg().addr_family(IpAddrFamily::Ipv6Only).build()); "ipv6 address family")]
#[test_case("trip example.com --addr-family ipv4-then-ipv6", Ok(cfg().addr_family(IpAddrFamily::Ipv4thenIpv6).build()); "ipv4 then ipv6 address family")]
#[test_case("trip example.com --addr-family ipv6-then-ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv6thenIpv4).build()); "ipv6 then ipv4 address family")]
#[test_case("trip example.com -F ipv4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "custom address family short")]
#[test_case("trip example.com --addr-family foo", Err(anyhow!("error: one of the values isn't valid for an argument")); "invalid address family")]
#[test_case("trip example.com -4", Ok(cfg().addr_family(IpAddrFamily::Ipv4Only).build()); "ipv4 address family shortcut")]
#[test_case("trip example.com -6", Ok(cfg().addr_family(IpAddrFamily::Ipv6Only).build()); "ipv6 address family shortcut")]
#[test_case("trip example.com -5", Err(anyhow!("error: unexpected argument found")); "invalid address family shortcut")]
#[test_case("trip example.com", Ok(cfg().first_ttl(1).build()); "default first ttl")]
#[test_case("trip example.com --first-ttl 5", Ok(cfg().first_ttl(5).build()); "custom first ttl")]
Expand Down Expand Up @@ -1316,7 +1366,7 @@ mod tests {
}
}

pub fn addr_family(self, addr_family: AddrFamily) -> Self {
pub fn addr_family(self, addr_family: IpAddrFamily) -> Self {
Self {
config: TrippyConfig {
addr_family,
Expand Down
23 changes: 19 additions & 4 deletions src/config/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::config::binding::TuiCommandItem;
use crate::config::theme::TuiThemeItem;
use crate::config::{
AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, LogFormat,
LogSpanEvents, Mode, MultipathStrategyConfig, ProtocolConfig, TuiColor, TuiKeyBinding,
AddressFamilyConfig, AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode,
LogFormat, LogSpanEvents, Mode, MultipathStrategyConfig, ProtocolConfig, TuiColor,
TuiKeyBinding,
};
use anyhow::anyhow;
use clap::builder::Styles;
Expand Down Expand Up @@ -60,12 +61,26 @@ pub struct Args {
)]
pub icmp: bool,

/// The address family [default: Ipv4thenIpv6]
#[arg(value_enum, short = 'F', long)]
pub addr_family: Option<AddressFamilyConfig>,

/// Use IPv4 only
#[arg(short = '4', long, conflicts_with = "ipv6")]
#[arg(
short = '4',
long,
conflicts_with = "ipv6",
conflicts_with = "addr_family"
)]
pub ipv4: bool,

/// Use IPv6 only
#[arg(short = '6', long, conflicts_with = "ipv4")]
#[arg(
short = '6',
long,
conflicts_with = "ipv4",
conflicts_with = "addr_family"
)]
pub ipv6: bool,

/// The target port (TCP & UDP only) [default: 80]
Expand Down
7 changes: 5 additions & 2 deletions src/config/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::config::{
AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode, LogFormat,
LogSpanEvents, Mode,
AddressFamilyConfig, AddressMode, AsMode, DnsResolveMethodConfig, GeoIpMode, IcmpExtensionMode,
LogFormat, LogSpanEvents, Mode,
};
use std::time::Duration;

Expand Down Expand Up @@ -61,6 +61,9 @@ 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;

/// The default value for `addr-family`.
pub const DEFAULT_ADDR_FAMILY: AddressFamilyConfig = AddressFamilyConfig::Ipv4ThenIpv6;

/// The default value for `dns-lookup-as-info`.
pub const DEFAULT_DNS_LOOKUP_AS_INFO: bool = false;

Expand Down
2 changes: 1 addition & 1 deletion src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl Default for ConfigStrategy {
fn default() -> Self {
Self {
protocol: Some(ProtocolConfig::from(defaults::DEFAULT_STRATEGY_PROTOCOL)),
addr_family: Some(AddressFamilyConfig::from(defaults::DEFAULT_ADDRESS_FAMILY)),
addr_family: Some(super::constants::DEFAULT_ADDR_FAMILY),
target_port: None,
source_port: None,
source_address: None,
Expand Down
2 changes: 1 addition & 1 deletion src/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
mod lazy_resolver;
mod resolver;

pub use lazy_resolver::{Config, DnsResolver, ResolveMethod};
pub use lazy_resolver::{Config, DnsResolver, IpAddrFamily, ResolveMethod};
pub use resolver::{AsInfo, DnsEntry, Error, Resolved, Resolver, Result, Unresolved};
Loading

0 comments on commit e5cce29

Please sign in to comment.