1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
use std::collections::HashSet;
use std::str::FromStr;
use anyhow::{anyhow, Result};
use aws_sdk_route53 as route53;
use aws_sdk_route53::types::{HostedZone, ResourceRecordSet, RrType};
use trust_dns_proto::rr::Name;
use crate::dns::suffixes;
use crate::hashable::Hashable;
pub trait Route53 {
fn route53(&self) -> &route53::Client;
}
pub async fn zone_for_domain<C>(aws_context: &C, name: &Name) -> Result<HostedZone>
where
C: Route53,
{
let names = suffixes(name.clone());
let mut zone = None;
let mut depth = None;
let mut zones = aws_context
.route53()
.list_hosted_zones()
.into_paginator()
.items()
.send();
while let Some(candidate_zone) = zones.try_next().await? {
let zone_name = Name::from_str(candidate_zone.name())?;
let match_position = names.iter().position(|name| *name == zone_name);
match (depth, match_position) {
(None, Some(matched_depth)) => {
zone = Some(candidate_zone.clone());
depth = Some(matched_depth);
}
(Some(found_depth), Some(matched_depth)) => {
if matched_depth < found_depth {
zone = Some(candidate_zone.clone());
depth = Some(matched_depth);
}
}
(_, _) => {}
}
}
zone.ok_or(anyhow!("No Route53 zone found for DNS suffix: {}", name))
}
pub async fn zone_actual_recordsets<C>(
aws_context: &C,
zone_id: &str,
dns_name: &Name,
) -> Result<HashSet<Hashable<ResourceRecordSet>>>
where
C: Route53,
{
let mut suffix_records = HashSet::new();
let mut next_record_name = Some(dns_name.to_ascii());
let mut next_record_type = None;
let mut next_record_identifier = None;
loop {
let records_resp = aws_context
.route53()
.list_resource_record_sets()
.hosted_zone_id(zone_id)
.set_start_record_name(next_record_name)
.set_start_record_type(next_record_type)
.set_start_record_identifier(next_record_identifier)
.send()
.await?;
let recordsets = records_resp.resource_record_sets();
for recordset in recordsets {
let recordset_name = recordset.name();
let recordset_name = Name::from_str(recordset_name)?;
if &recordset_name != dns_name {
break;
}
if [RrType::A, RrType::Aaaa].contains(recordset.r#type()) {
suffix_records.insert(recordset.clone().into());
}
}
if records_resp.is_truncated() {
next_record_name = records_resp.next_record_name().map(String::from);
next_record_type = records_resp.next_record_type().cloned();
next_record_identifier = records_resp.next_record_identifier().map(String::from);
} else {
break;
}
}
Ok(suffix_records)
}
|