-
Notifications
You must be signed in to change notification settings - Fork 1
/
dnspacket.rs
137 lines (125 loc) · 5.19 KB
/
dnspacket.rs
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use std::net::Ipv4Addr;
use crate::protocol::byte_packet_buffer::BytePacketBuffer;
use crate::protocol::dnsheader::DnsHeader;
use crate::protocol::dnsquestion::DnsQuestion;
use crate::protocol::dnsrecord::DnsRecord;
use crate::protocol::querytype::QueryType;
use crate::protocol::resultcode::ResultCode;
#[derive(Clone, Debug)]
pub struct DnsPacket {
pub header: DnsHeader,
pub questions: Vec<DnsQuestion>,
pub answers: Vec<DnsRecord>,
pub authorities: Vec<DnsRecord>,
pub resources: Vec<DnsRecord>,
}
impl DnsPacket {
pub fn new() -> DnsPacket {
DnsPacket {
header: DnsHeader::new(),
questions: Vec::new(),
answers: Vec::new(),
authorities: Vec::new(),
resources: Vec::new(),
}
}
pub fn from_buffer(buffer: &mut BytePacketBuffer) -> Result<DnsPacket, Box<dyn std::error::Error>> {
let mut result = DnsPacket::new();
result.header.read(buffer)?;
for _ in 0..result.header.questions {
let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0));
question.read(buffer)?;
result.questions.push(question);
}
for _ in 0..result.header.answers {
let rec = DnsRecord::read(buffer)?;
result.answers.push(rec);
}
for _ in 0..result.header.authoritative_entries {
let rec = DnsRecord::read(buffer)?;
result.authorities.push(rec);
}
for _ in 0..result.header.resource_entries {
let rec = DnsRecord::read(buffer)?;
result.resources.push(rec);
}
Ok(result)
}
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<() ,Box<dyn std::error::Error>> {
self.header.questions = self.questions.len() as u16;
self.header.answers = self.answers.len() as u16;
self.header.authoritative_entries = self.authorities.len() as u16;
self.header.resource_entries = self.resources.len() as u16;
self.header.write(buffer)?;
for question in &self.questions {
question.write(buffer)?;
}
for rec in &self.answers {
rec.write(buffer)?;
}
for rec in &self.authorities {
rec.write(buffer)?;
}
for rec in &self.resources {
rec.write(buffer)?;
}
Ok(())
}
//It is useful to be able to pick a random A record from a packet,
//since when we get multiple nIP's for a single name, it doesn't matter which one we use.
//Isliye random A record pick karne ke liye ye function banaya hai.
pub fn get_random_a(&self) -> Option<Ipv4Addr> {
self.answers.iter()
.filter_map(|record| match record {
DnsRecord::A {addr, ..} => Some(*addr),
_ => None,
})
.next()
}
//A helper function which returns an iterator over all name servers
//in the authorities section, represented as (domain , host) tuples.
pub fn get_ns<'a>(&'a self , qname : &'a str) -> impl Iterator<Item = (&'a str , &'a str)> {
self.authorities.iter()
//In practice, these are always NS records in well formed packages.
//Convert the NS records to a tuple which has only the data we need
//to make it easy to work with
.filter_map(|record| match record {
DnsRecord::NS { domain, host, .. } => Some((domain.as_str(), host.as_str())),
_ => None,
})
//Filter out the records which are not for the domain we are looking for
.filter(move |(domain , _)| qname.ends_with(*domain))
}
//We will use the fact that name servers often bundle the corresponding A records
//When repluing to an NS query to implement a function that returns the actual IP
//for an NS record if possible.
pub fn get_resolved_ns (&self , qname : &str) -> Option<Ipv4Addr> {
//Get an iterator over the nameservers in the authorities section
self.get_ns(qname)
//Now we need to look for a matching A record in the additional section.
//Scince we just want the first one, we can just build a stream of matching records.
.flat_map(|(_, host)| {
self.resources.iter()
// Filter for A records where the domain match the host
// of the NS record that we are currently processing
.filter_map(move |record| match record {
DnsRecord::A { domain , addr , ..} if domain == host => Some(*addr),
_ => None,
})
})
.map(| addr | addr)
//Finally pick the first valid entry
.next()
}
/// However, not all name servers are as that nice. In certain cases there won't
/// be any A records in the additional section, and we'll have to perform *another*
/// lookup in the midst. For this, we introduce a method for returning the host
/// name of an appropriate name server.
pub fn get_unresolved_ns<'a>(&'a self, qname: &'a str) -> Option<&'a str> {
// Get an iterator over the nameservers in the authorities section
self.get_ns(qname)
.map(|(_, host)| host)
// Finally, pick the first valid entry
.next()
}
}