use crate::ipc_types::{ResolveParams, ResolveResult}; use crate::resolver_udp::decode_answers; use rustdns_protocol::packet::{DnsPacket, DnsQuestion}; use rustdns_protocol::types::{QClass, QType, EDNS_DO_BIT, FLAG_RD}; use std::time::Duration; use tracing::debug; /// Resolve a DNS query via DNS-over-HTTPS (RFC 8484 wire format). pub async fn resolve_doh( params: &ResolveParams, http_client: &reqwest::Client, ) -> Result { let qtype = QType::from_str(¶ms.record_type); let id: u16 = rand::random(); // Build query packet (same as UDP) let mut query = DnsPacket::new_query(id); query.flags = FLAG_RD; query.questions.push(DnsQuestion { name: params.name.clone(), qtype, qclass: QClass::IN, }); // Add OPT record with DO bit for DNSSEC query.additionals.push(rustdns_protocol::packet::DnsRecord { name: ".".to_string(), rtype: QType::OPT, rclass: QClass::from_u16(4096), ttl: 0, rdata: vec![], opt_flags: Some(EDNS_DO_BIT), }); let query_bytes = query.encode(); let timeout = Duration::from_millis(params.timeout_ms); let response = http_client .post(¶ms.doh_url) .header("Content-Type", "application/dns-message") .header("Accept", "application/dns-message") .body(query_bytes) .timeout(timeout) .send() .await .map_err(|e| format!("DoH request failed: {}", e))?; if !response.status().is_success() { return Err(format!("DoH server returned status {}", response.status())); } let response_bytes = response .bytes() .await .map_err(|e| format!("Failed to read DoH response body: {}", e))?; let dns_response = DnsPacket::parse(&response_bytes) .map_err(|e| format!("Failed to parse DoH response: {}", e))?; debug!( "DoH response: id={}, rcode={}, answers={}, ad={}", dns_response.id, dns_response.rcode(), dns_response.answers.len(), dns_response.has_ad_flag() ); let answers = decode_answers(&dns_response.answers, &response_bytes); Ok(ResolveResult { answers, ad_flag: dns_response.has_ad_flag(), rcode: dns_response.rcode(), }) }