// Typed convenience APIs and DNS record presentation helpers.
///|
pub async fn Resolver::resolve_a(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_a) {
Ok(value) => value
Err(error) => return Err(error)
}
let ips : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
A(ip) => ips.push(format_ipv4(ip))
_ => ()
}
}
Ok(ips)
}
///|
pub async fn Resolver::resolve_aaaa(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_aaaa) {
Ok(value) => value
Err(error) => return Err(error)
}
let ips : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
AAAA(w1, w2, w3, w4) => ips.push(format_ipv6(w1, w2, w3, w4))
_ => ()
}
}
Ok(ips)
}
///|
pub async fn Resolver::resolve_mx(
self : Resolver,
name : String,
) -> Result[Array[(Int, String)], ResolveError] {
let result = match self.resolve(name, qtype_mx) {
Ok(value) => value
Err(error) => return Err(error)
}
let entries : Array[(Int, String)] = Array::new(
capacity=result.answers.length(),
)
for rr in result.answers {
match rr.rdata {
MX(preference, exchange) =>
entries.push((preference, strip_trailing_dot(exchange)))
_ => ()
}
}
entries.sort_by((left, right) => left.0 - right.0)
Ok(entries)
}
///|
pub async fn Resolver::resolve_ns(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_ns) {
Ok(value) => value
Err(error) => return Err(error)
}
let names : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
NS(target) => names.push(strip_trailing_dot(target))
_ => ()
}
}
Ok(names)
}
///|
pub async fn Resolver::resolve_cname(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_cname) {
Ok(value) => value
Err(error) => return Err(error)
}
let names : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
CNAME(target) => names.push(strip_trailing_dot(target))
_ => ()
}
}
Ok(names)
}
///|
pub async fn Resolver::resolve_txt(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_txt) {
Ok(value) => value
Err(error) => return Err(error)
}
let texts : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
TXT(strings) => texts.push(strings.join(""))
_ => ()
}
}
Ok(texts)
}
///|
pub(all) struct SoaResult {
mname : String
rname : String
serial : UInt
refresh : UInt
retry : UInt
expire : UInt
minimum : UInt
}
///|
pub async fn Resolver::resolve_soa(
self : Resolver,
name : String,
) -> Result[Array[SoaResult], ResolveError] {
let result = match self.resolve(name, qtype_soa) {
Ok(value) => value
Err(error) => return Err(error)
}
let values : Array[SoaResult] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
SOA(mname, rname, serial, refresh, retry, expire, minimum) =>
values.push({
mname: strip_trailing_dot(mname),
rname: strip_trailing_dot(rname),
serial,
refresh,
retry,
expire,
minimum,
})
_ => ()
}
}
Ok(values)
}
///|
pub async fn Resolver::resolve_ptr(
self : Resolver,
name : String,
) -> Result[Array[String], ResolveError] {
let result = match self.resolve(name, qtype_ptr) {
Ok(value) => value
Err(error) => return Err(error)
}
let names : Array[String] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
PTR(target) => names.push(strip_trailing_dot(target))
_ => ()
}
}
Ok(names)
}
///|
pub(all) struct SrvResult {
priority : UInt
weight : UInt
port : UInt
target : String
}
///|
pub async fn Resolver::resolve_srv(
self : Resolver,
name : String,
) -> Result[Array[SrvResult], ResolveError] {
let result = match self.resolve(name, qtype_srv) {
Ok(value) => value
Err(error) => return Err(error)
}
let values : Array[SrvResult] = Array::new(capacity=result.answers.length())
for rr in result.answers {
match rr.rdata {
SRV(priority, weight, port, target) =>
values.push({
priority,
weight,
port,
target: strip_trailing_dot(target),
})
_ => ()
}
}
// Deterministic presentation order. RFC 2782 weighted selection belongs to
// connection establishment, so this API exposes all candidates instead.
values.sort_by((left, right) => {
let priority = left.priority.reinterpret_as_int() -
right.priority.reinterpret_as_int()
if priority != 0 {
priority
} else {
right.weight.reinterpret_as_int() - left.weight.reinterpret_as_int()
}
})
Ok(values)
}
///|
pub async fn Resolver::resolve_ipv4(
self : Resolver,
host : String,
) -> Result[String, ResolveError] {
let addresses = match self.resolve_a(host) {
Ok(value) => value
Err(error) => return Err(error)
}
match addresses.get(0) {
Some(address) => Ok(address)
None => Err(NoData)
}
}
///|
fn format_ipv4(ip : Int) -> String {
let a = (ip >> 24) & 0xFF
let b = (ip >> 16) & 0xFF
let c = (ip >> 8) & 0xFF
let d = ip & 0xFF
a.to_string() +
"." +
b.to_string() +
"." +
c.to_string() +
"." +
d.to_string()
}
///|
fn format_ipv6(w1 : Int, w2 : Int, w3 : Int, w4 : Int) -> String {
let groups = [
hex16((w1 >> 16) & 0xFFFF),
hex16(w1 & 0xFFFF),
hex16((w2 >> 16) & 0xFFFF),
hex16(w2 & 0xFFFF),
hex16((w3 >> 16) & 0xFFFF),
hex16(w3 & 0xFFFF),
hex16((w4 >> 16) & 0xFFFF),
hex16(w4 & 0xFFFF),
]
groups.join(":")
}
///|
fn hex16(value : Int) -> String {
if value == 0 {
return "0"
}
let out = StringBuilder()
let mut shift = 12
let started = Ref(false)
while shift >= 0 {
let nibble = (value >> shift) & 0xF
if nibble != 0 || started.val {
started.val = true
out.write_char(
if nibble < 10 {
(b'0'.to_int() + nibble).unsafe_to_char()
} else {
(b'a'.to_int() + nibble - 10).unsafe_to_char()
},
)
}
shift = shift - 4
}
out.to_string()
}
///|
fn strip_trailing_dot(name : String) -> String {
if name.length() > 0 && name[name.length() - 1] == '.' {
name[:name.length() - 1].to_owned()
} else {
name
}
}