feat(rustdns-client): add Rust DNS client binary and TypeScript IPC bridge to enable UDP and DoH resolution, RDATA decoding, and DNSSEC AD/rcode support
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
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(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user