Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Andy dev #90

Merged
merged 16 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ http = "1.0"
# hyper (http implementation used by axum)
hyper = { version = "1.0", features = ["full"] }

# JSONPath
jsonpath-rust = "=0.5.0"
jsonpath_lib = "0.3.0"

# internationalized domain names for applications
idna = "0.5"

Expand Down
83 changes: 71 additions & 12 deletions icann-rdap-cli/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,33 @@ use icann_rdap_client::query::{
qtype::QueryType,
};
use icann_rdap_common::{
cache::HttpData,
httpdata::HttpData,
iana::{BootstrapRegistry, IanaRegistry, IanaRegistryType},
};
use reqwest::Client;
use tracing::debug;

use crate::{dirs::bootstrap_cache_path, error::CliError};

/// Defines the type of bootstrapping to use.
pub(crate) enum BootstrapType {
None,
/// Use RFC 9224 bootstrapping.
///
/// This is the typical bootstrapping for RDAP as defined by RFC 9224.
Rfc9224,

/// Use the supplied URL.
///
/// Essentially, this means no bootstrapping as the client is being given
/// a full URL.
Url(String),
Tag(String),

/// Use a hint.
///
/// This will try to find an authoritative server by cycling through the various
/// bootstrap registries in the following order: object tags, TLDs, IP addresses,
/// ASNs.
Hint(String),
}

pub(crate) async fn get_base_url(
Expand All @@ -40,17 +55,61 @@ pub(crate) async fn get_base_url(
let store = FileCacheBootstrapStore;

match bootstrap_type {
BootstrapType::None => Ok(qtype_to_bootstrap_url(client, &store, query_type, |reg| {
BootstrapType::Rfc9224 => Ok(qtype_to_bootstrap_url(client, &store, query_type, |reg| {
debug!("Fetching IANA registry {}", reg.url())
})
.await?),
BootstrapType::Url(url) => Ok(url.to_owned()),
BootstrapType::Tag(tag) => {
BootstrapType::Hint(hint) => {
fetch_bootstrap(&IanaRegistryType::RdapObjectTags, client, &store, |_reg| {
debug!("Fetching IANA RDAP Object Tag Registry")
})
.await?;
Ok(store.get_tag_urls(tag)?.preferred_url()?)
if let Ok(urls) = store.get_tag_urls(hint) {
Ok(urls.preferred_url()?)
} else {
fetch_bootstrap(
&IanaRegistryType::RdapBootstrapDns,
client,
&store,
|_reg| debug!("Fetching IANA RDAP DNS Registry"),
)
.await?;
if let Ok(urls) = store.get_dns_urls(hint) {
Ok(urls.preferred_url()?)
} else {
fetch_bootstrap(
&IanaRegistryType::RdapBootstrapIpv4,
client,
&store,
|_reg| debug!("Fetching IANA RDAP IPv4 Registry"),
)
.await?;
if let Ok(urls) = store.get_ipv4_urls(hint) {
Ok(urls.preferred_url()?)
} else {
fetch_bootstrap(
&IanaRegistryType::RdapBootstrapIpv6,
client,
&store,
|_reg| debug!("Fetching IANA RDAP IPv6 Registry"),
)
.await?;
if let Ok(urls) = store.get_ipv6_urls(hint) {
Ok(urls.preferred_url()?)
} else {
fetch_bootstrap(
&IanaRegistryType::RdapBootstrapAsn,
client,
&store,
|_reg| debug!("Fetching IANA RDAP ASN Registry"),
)
.await?;
Ok(store.get_asn_urls(hint)?.preferred_url()?)
}
}
}
}
}
}
}
Expand Down Expand Up @@ -138,7 +197,7 @@ where
mod test {
use icann_rdap_client::query::{bootstrap::PreferredUrl, qtype::QueryType};
use icann_rdap_common::{
cache::HttpData,
httpdata::HttpData,
iana::{IanaRegistry, IanaRegistryType},
};
use serial_test::serial;
Expand Down Expand Up @@ -190,7 +249,7 @@ mod test {
bs.put_bootstrap_registry(
&IanaRegistryType::RdapBootstrapDns,
iana,
HttpData::now().host("example.com").build(),
HttpData::example().build(),
)
.expect("put iana registry");

Expand Down Expand Up @@ -244,7 +303,7 @@ mod test {
bs.put_bootstrap_registry(
&IanaRegistryType::RdapBootstrapAsn,
iana,
HttpData::now().host("example.com").build(),
HttpData::example().build(),
)
.expect("put iana registry");

Expand Down Expand Up @@ -298,7 +357,7 @@ mod test {
bs.put_bootstrap_registry(
&IanaRegistryType::RdapBootstrapIpv4,
iana,
HttpData::now().host("example.com").build(),
HttpData::example().build(),
)
.expect("put iana registry");

Expand Down Expand Up @@ -352,7 +411,7 @@ mod test {
bs.put_bootstrap_registry(
&IanaRegistryType::RdapBootstrapIpv6,
iana,
HttpData::now().host("example.com").build(),
HttpData::example().build(),
)
.expect("put iana registry");

Expand Down Expand Up @@ -409,7 +468,7 @@ mod test {
bs.put_bootstrap_registry(
&IanaRegistryType::RdapObjectTags,
iana,
HttpData::now().host("example.com").build(),
HttpData::example().build(),
)
.expect("put iana registry");

Expand Down
89 changes: 76 additions & 13 deletions icann-rdap-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use clap::builder::Styles;
use icann_rdap_common::check::CheckClass;
use icann_rdap_common::client::create_client;
use icann_rdap_common::client::ClientConfig;
use query::InrBackupBootstrap;
use query::ProcessType;
use query::ProcessingParams;
use query::TldLookup;
use std::io::IsTerminal;
use std::str::FromStr;
use tracing::error;
Expand Down Expand Up @@ -93,9 +95,11 @@ struct Cli {

/// An RDAP base signifier.
///
/// This option gets a base URL from the RDAP bootstrap registry maintained
/// This option gets a base URL from the RDAP bootstrap registries maintained
/// by IANA. For example, using "com" will get the base URL for the .com
/// registry.
/// registry, and "arin" will get the base URL for the RDAP tags registry,
/// which points to the ARIN RIR. This option checks the bootstrap registries
/// in the following order: object tags, TLDs, IPv4, IPv6, ASN.
#[arg(short = 'b', long, required = false, env = "RDAP_BASE")]
base: Option<String>,

Expand All @@ -107,6 +111,31 @@ struct Cli {
#[arg(short = 'B', long, required = false, env = "RDAP_BASE_URL")]
base_url: Option<String>,

/// Specify where to send TLD queries.
///
/// Defaults to IANA.
#[arg(
long,
required = false,
env = "RDAP_TLD_LOOKUP",
value_enum,
default_value_t = TldLookupArg::Iana,
)]
tld_lookup: TldLookupArg,

/// Specify a backup INR bootstrap.
///
/// This is used as a backup when the bootstrapping process cannot find an authoritative
/// server for IP addresses and Autonomous System Numbers. Defaults to ARIN.
#[arg(
long,
required = false,
env = "RDAP_INR_BACKUP_BOOTSTRAP",
value_enum,
default_value_t = InrBackupBootstrapArg::Arin,
)]
inr_backup_bootstrap: InrBackupBootstrapArg,

/// Output format.
///
/// This option determines the format of the result.
Expand Down Expand Up @@ -317,11 +346,17 @@ enum CheckTypeArg {
/// Informational items.
Info,

/// Checks for specification warnings.
/// Checks for STD 95 warnings.
SpecWarn,

/// Checks for specficiation errors.
/// Checks for STD 95 errors.
SpecError,

/// Cidr0 errors.
Cidr0Error,

/// ICANN Profile errors.
IcannError,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
Expand Down Expand Up @@ -366,6 +401,24 @@ enum PagerType {
Auto,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum TldLookupArg {
/// Use IANA for TLD lookups.
Iana,

/// No TLD specific lookups.
None,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum InrBackupBootstrapArg {
/// Use ARIN when no INR bootstrap can be found.
Arin,

/// No backup for INR bootstraps.
None,
}

impl From<&LogLevel> for LevelFilter {
fn from(log_level: &LogLevel) -> Self {
match log_level {
Expand Down Expand Up @@ -395,11 +448,7 @@ pub async fn wrapped_main() -> Result<(), CliError> {
let cli = Cli::parse();

if cli.reset {
// TODO uncomment once #10 is complete.
// info!("Removing cache files and resetting configuration.");
dirs::reset()?;
// TODO uncomment once #10 is complete.
// info!("Exiting after reset.");
return Ok(());
}

Expand Down Expand Up @@ -450,23 +499,37 @@ pub async fn wrapped_main() -> Result<(), CliError> {
CheckTypeArg::Info => CheckClass::Informational,
CheckTypeArg::SpecWarn => CheckClass::SpecificationWarning,
CheckTypeArg::SpecError => CheckClass::SpecificationError,
CheckTypeArg::Cidr0Error => CheckClass::Cidr0Error,
CheckTypeArg::IcannError => CheckClass::IcannError,
})
.collect::<Vec<CheckClass>>()
};

let bootstrap_type = if let Some(tag) = cli.base {
BootstrapType::Tag(tag)
} else if let Some(base_url) = cli.base_url {
BootstrapType::Url(base_url)
let bootstrap_type = if let Some(ref tag) = cli.base {
BootstrapType::Hint(tag.to_string())
} else if let Some(ref base_url) = cli.base_url {
BootstrapType::Url(base_url.to_string())
} else {
BootstrapType::None
BootstrapType::Rfc9224
};

let tld_lookup = match cli.tld_lookup {
TldLookupArg::Iana => TldLookup::Iana,
TldLookupArg::None => TldLookup::None,
};

let inr_backup_bootstrap = match cli.inr_backup_bootstrap {
InrBackupBootstrapArg::Arin => InrBackupBootstrap::Arin,
InrBackupBootstrapArg::None => InrBackupBootstrap::None,
};

let processing_params = ProcessingParams {
bootstrap_type,
output_type,
check_types,
process_type,
tld_lookup,
inr_backup_bootstrap,
error_on_checks: cli.error_on_checks,
no_cache: cli.no_cache,
max_cache_age: cli.max_cache_age,
Expand Down
Loading
Loading