feature: Added resolve binary for handle resolution debugging

+42
src/bin/resolve.rs
··· 1 + use std::env; 2 + 3 + use anyhow::Result; 4 + use smokesignal::config::{default_env, optional_env, version, CertificateBundles, DnsNameservers}; 5 + use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _}; 6 + 7 + #[tokio::main] 8 + async fn main() -> Result<()> { 9 + tracing_subscriber::registry() 10 + .with(tracing_subscriber::EnvFilter::new( 11 + std::env::var("RUST_LOG").unwrap_or_else(|_| "trace".into()), 12 + )) 13 + .with(tracing_subscriber::fmt::layer().pretty()) 14 + .init(); 15 + 16 + let certificate_bundles: CertificateBundles = optional_env("CERTIFICATE_BUNDLES").try_into()?; 17 + let default_user_agent = format!("smokesignal ({}; +https://smokesignal.events/)", version()?); 18 + let user_agent = default_env("USER_AGENT", &default_user_agent); 19 + let dns_nameservers: DnsNameservers = optional_env("DNS_NAMESERVERS").try_into()?; 20 + 21 + let mut client_builder = reqwest::Client::builder(); 22 + for ca_certificate in certificate_bundles.as_ref() { 23 + tracing::info!("Loading CA certificate: {:?}", ca_certificate); 24 + let cert = std::fs::read(ca_certificate)?; 25 + let cert = reqwest::Certificate::from_pem(&cert)?; 26 + client_builder = client_builder.add_root_certificate(cert); 27 + } 28 + 29 + client_builder = client_builder.user_agent(user_agent); 30 + let http_client = client_builder.build()?; 31 + 32 + // Initialize the DNS resolver with configuration from the app config 33 + let dns_resolver = smokesignal::resolve::create_resolver(dns_nameservers); 34 + 35 + for subject in env::args() { 36 + let resolved_did = 37 + smokesignal::resolve::resolve_subject(&http_client, &dns_resolver, &subject).await; 38 + tracing::info!(?resolved_did, ?subject, "resolved subject"); 39 + } 40 + 41 + Ok(()) 42 + }
+1 -1
src/bin/smokesignal.rs
··· 80 80 let jinja = reload_env::build_env(&config.external_base, &config.version); 81 81 82 82 // Initialize the DNS resolver with configuration from the app config 83 - let dns_resolver = create_resolver(&config); 83 + let dns_resolver = create_resolver(config.dns_nameservers.clone()); 84 84 85 85 let web_context = WebContext::new( 86 86 pool.clone(),
+4 -4
src/config.rs
··· 56 56 let http_cookie_key: HttpCookieKey = 57 57 require_env("HTTP_COOKIE_KEY").and_then(|value| value.try_into())?; 58 58 59 - let http_static_path = default_env("HTTP_STATIC_PATH", "static").try_into()?; 59 + let http_static_path = default_env("HTTP_STATIC_PATH", "static"); 60 60 61 61 let external_base = require_env("EXTERNAL_BASE")?; 62 62 ··· 134 134 } 135 135 } 136 136 137 - fn require_env(name: &str) -> Result<String> { 137 + pub fn require_env(name: &str) -> Result<String> { 138 138 std::env::var(name).map_err(|_| ConfigError::EnvVarRequired(name.to_string()).into()) 139 139 } 140 140 141 - fn optional_env(name: &str) -> String { 141 + pub fn optional_env(name: &str) -> String { 142 142 std::env::var(name).unwrap_or("".to_string()) 143 143 } 144 144 145 - fn default_env(name: &str, default_value: &str) -> String { 145 + pub fn default_env(name: &str, default_value: &str) -> String { 146 146 std::env::var(name).unwrap_or(default_value.to_string()) 147 147 } 148 148
+5 -3
src/did.rs
··· 4 4 use serde_json::Value; 5 5 use std::collections::HashMap; 6 6 7 - #[derive(Clone, Deserialize)] 7 + #[derive(Clone, Deserialize, Debug)] 8 8 #[serde(rename_all = "camelCase")] 9 9 pub struct Service { 10 10 pub id: String, ··· 14 14 pub service_endpoint: String, 15 15 } 16 16 17 - #[derive(Clone, Deserialize)] 17 + #[derive(Clone, Deserialize, Debug)] 18 18 #[serde(tag = "type", rename_all = "camelCase")] 19 19 pub enum VerificationMethod { 20 20 Multikey { ··· 30 30 }, 31 31 } 32 32 33 - #[derive(Clone, Deserialize)] 33 + #[derive(Clone, Deserialize, Debug)] 34 34 #[serde(rename_all = "camelCase")] 35 35 pub struct Document { 36 36 pub id: String, ··· 209 209 210 210 pub async fn query_hostname(http_client: &reqwest::Client, hostname: &str) -> Result<Document> { 211 211 let url = format!("https://{}/.well-known/did.json", hostname); 212 + 213 + tracing::debug!(?url, "query_hostname"); 212 214 213 215 http_client 214 216 .get(&url)
+1 -1
src/jose.rs
··· 97 97 Signature::try_from(signature_bytes.as_slice()).map_err(|_| JoseError::InvalidSignature)?; 98 98 99 99 // Verify signature 100 - let verifying_key = VerifyingKey::from(public_key.clone()); 100 + let verifying_key = VerifyingKey::from(public_key); 101 101 let content = format!("{}.{}", encoded_header, encoded_claims); 102 102 103 103 verifying_key
+13 -3
src/resolve.rs
··· 8 8 use std::collections::HashSet; 9 9 use std::time::Duration; 10 10 11 - use crate::config::Config; 11 + use crate::config::DnsNameservers; 12 12 use crate::did::web::query_hostname; 13 13 14 14 pub enum InputType { ··· 111 111 ) 112 112 .await; 113 113 114 + tracing::debug!( 115 + ?handle, 116 + ?dns_lookup, 117 + ?http_lookup, 118 + ?did_web_lookup, 119 + "raw query results" 120 + ); 121 + 114 122 let did_web_lookup_did = did_web_lookup 115 123 .map(|document| document.id) 116 124 .map_err(ResolveError::DIDWebResolutionFailed); ··· 122 130 if results.is_empty() { 123 131 return Err(ResolveError::NoDIDsFound); 124 132 } 133 + 134 + tracing::debug!(?handle, ?results, "query results"); 125 135 126 136 let first = results[0].clone(); 127 137 if results.iter().all(|result| result == &first) { ··· 145 155 /// 146 156 /// If custom nameservers are configured in app config, they will be used. 147 157 /// Otherwise, the system default resolver configuration will be used. 148 - pub fn create_resolver(config: &Config) -> TokioAsyncResolver { 158 + pub fn create_resolver(nameservers: DnsNameservers) -> TokioAsyncResolver { 149 159 // Initialize the DNS resolver with custom nameservers if configured 150 - let nameservers = config.dns_nameservers.as_ref(); 160 + let nameservers = nameservers.as_ref(); 151 161 let resolver_config = if !nameservers.is_empty() { 152 162 // Use custom nameservers 153 163 tracing::info!("Using custom DNS nameservers: {:?}", nameservers);