76 lines
2.3 KiB
Rust
76 lines
2.3 KiB
Rust
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<ResolveResult, String> {
|
|
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(),
|
|
})
|
|
}
|