Skip to content

Commit

Permalink
Reimplement WASM TXT lookup to use DNS wireformat
Browse files Browse the repository at this point in the history
  • Loading branch information
dangeross committed Feb 26, 2025
1 parent 17d16db commit fc3138a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 36 deletions.
11 changes: 11 additions & 0 deletions libs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions libs/sdk-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ tonic = { workspace = true, features = [
] }

[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
dns-parser = "0.8.0"
getrandom = { version = "0.2.14", features = ["js"] }
prost = "^0.13"
tonic = { version = "0.12", default-features = false, features = ["codegen", "prost"] }
Expand Down
74 changes: 38 additions & 36 deletions libs/sdk-common/src/dns_resolver/resolver_wasm.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
use std::time::Duration;

use anyhow::{anyhow, Result};
use serde::Deserialize;
use dns_parser::{Builder, Packet, RData, ResponseCode};
use dns_parser::{QueryClass, QueryType};

use crate::utils::rest_client;

/// Response structure according to <https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json/#response-fields>
#[derive(Debug, Deserialize)]
pub struct Response {
#[serde(rename = "Status")]
pub status: i32,
#[serde(default)]
#[serde(rename = "Answer")]
pub answer: Vec<Answer>,
}

#[derive(Debug, Deserialize)]
pub struct Answer {
pub name: String,
#[serde(rename = "type")]
pub record_type: i32,
pub data: String,
}

pub(crate) async fn txt_lookup(dns_name: String) -> Result<Vec<String>> {
let url = format!("https://cloudflare-dns.com/dns-query?name={dns_name}&type=TXT");
let raw_body = rest_client::get_reqwest_client()?
.get(url)
.header("Accept", "application/dns-json")
.timeout(Duration::from_secs(30))
let mut builder = Builder::new_query(1, true);
builder.add_question(&dns_name, false, QueryType::TXT, QueryClass::IN);
let req_bytes = builder
.build()
.map_err(|_| anyhow!("Error building DNS query"))?;
let client = rest_client::get_reqwest_client()?;
let res_bytes = client
.post(format!("https://cloudflare-dns.com/dns-query"))
.body(req_bytes)
.header("Accept", "application/dns-message")
.header("Content-Type", "application/dns-message")
.send()
.await?
.error_for_status()?
.text()
.bytes()
.await?;
let res: Response = serde_json::from_str(&raw_body)?;
match res.status {
0 => Ok(res
.answer
.into_iter()
.filter(|a| a.name == dns_name && a.record_type == 16)
.map(|a| a.data.replace("\"", ""))
.collect()),
n => Err(anyhow!("Error response received from DNS service: {n}")),
let packet = Packet::parse(&res_bytes)?;
if packet.header.response_code != ResponseCode::NoError {
return Err(anyhow!(
"Received error response from DNS query: {}",
packet.header.response_code
));
}
let res = packet
.answers
.into_iter()
.filter_map(|answer| {
if let RData::TXT(txt) = answer.data {
Some(
txt.iter()
.filter_map(|t| std::str::from_utf8(t).ok())
.collect::<Vec<_>>()
.concat(),
)
} else {
None
}
})
.collect::<Vec<_>>();

Ok(res)
}
11 changes: 11 additions & 0 deletions tools/sdk-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fc3138a

Please sign in to comment.