250 lines
7.2 KiB
Rust
250 lines
7.2 KiB
Rust
use std::net::Ipv4Addr;
|
|
use std::net::Ipv6Addr;
|
|
|
|
use crate::buffer::BytePacketBuffer;
|
|
use crate::question::QueryType;
|
|
use crate::Result;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
#[allow(dead_code)]
|
|
pub enum DnsRecord {
|
|
UNKNOWN {
|
|
domain: String,
|
|
qtype: u16,
|
|
data_len: u16,
|
|
ttl: u32,
|
|
},
|
|
A {
|
|
domain: String,
|
|
addr: Ipv4Addr,
|
|
ttl: u32,
|
|
},
|
|
NS {
|
|
domain: String,
|
|
host: String,
|
|
ttl: u32,
|
|
},
|
|
CNAME {
|
|
domain: String,
|
|
host: String,
|
|
ttl: u32,
|
|
},
|
|
MX {
|
|
domain: String,
|
|
priority: u16,
|
|
host: String,
|
|
ttl: u32,
|
|
},
|
|
AAAA {
|
|
domain: String,
|
|
addr: Ipv6Addr,
|
|
ttl: u32,
|
|
},
|
|
}
|
|
|
|
impl DnsRecord {
|
|
pub fn ttl(&self) -> u32 {
|
|
match self {
|
|
DnsRecord::A { ttl, .. }
|
|
| DnsRecord::NS { ttl, .. }
|
|
| DnsRecord::CNAME { ttl, .. }
|
|
| DnsRecord::MX { ttl, .. }
|
|
| DnsRecord::AAAA { ttl, .. }
|
|
| DnsRecord::UNKNOWN { ttl, .. } => *ttl,
|
|
}
|
|
}
|
|
|
|
pub fn set_ttl(&mut self, new_ttl: u32) {
|
|
match self {
|
|
DnsRecord::A { ttl, .. }
|
|
| DnsRecord::NS { ttl, .. }
|
|
| DnsRecord::CNAME { ttl, .. }
|
|
| DnsRecord::MX { ttl, .. }
|
|
| DnsRecord::AAAA { ttl, .. }
|
|
| DnsRecord::UNKNOWN { ttl, .. } => *ttl = new_ttl,
|
|
}
|
|
}
|
|
|
|
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
|
|
let mut domain = String::new();
|
|
buffer.read_qname(&mut domain)?;
|
|
|
|
let qtype_num = buffer.read_u16()?;
|
|
let qtype = QueryType::from_num(qtype_num);
|
|
let _ = buffer.read_u16()?;
|
|
let ttl = buffer.read_u32()?;
|
|
let data_len = buffer.read_u16()?;
|
|
|
|
match qtype {
|
|
QueryType::A => {
|
|
let raw_addr = buffer.read_u32()?;
|
|
let addr = Ipv4Addr::new(
|
|
((raw_addr >> 24) & 0xFF) as u8,
|
|
((raw_addr >> 16) & 0xFF) as u8,
|
|
((raw_addr >> 8) & 0xFF) as u8,
|
|
(raw_addr & 0xFF) as u8,
|
|
);
|
|
|
|
Ok(DnsRecord::A { domain, addr, ttl })
|
|
}
|
|
QueryType::AAAA => {
|
|
let raw_addr1 = buffer.read_u32()?;
|
|
let raw_addr2 = buffer.read_u32()?;
|
|
let raw_addr3 = buffer.read_u32()?;
|
|
let raw_addr4 = buffer.read_u32()?;
|
|
let addr = Ipv6Addr::new(
|
|
((raw_addr1 >> 16) & 0xFFFF) as u16,
|
|
(raw_addr1 & 0xFFFF) as u16,
|
|
((raw_addr2 >> 16) & 0xFFFF) as u16,
|
|
(raw_addr2 & 0xFFFF) as u16,
|
|
((raw_addr3 >> 16) & 0xFFFF) as u16,
|
|
(raw_addr3 & 0xFFFF) as u16,
|
|
((raw_addr4 >> 16) & 0xFFFF) as u16,
|
|
(raw_addr4 & 0xFFFF) as u16,
|
|
);
|
|
|
|
Ok(DnsRecord::AAAA { domain, addr, ttl })
|
|
}
|
|
QueryType::NS => {
|
|
let mut ns = String::new();
|
|
buffer.read_qname(&mut ns)?;
|
|
|
|
Ok(DnsRecord::NS {
|
|
domain,
|
|
host: ns,
|
|
ttl,
|
|
})
|
|
}
|
|
QueryType::CNAME => {
|
|
let mut cname = String::new();
|
|
buffer.read_qname(&mut cname)?;
|
|
|
|
Ok(DnsRecord::CNAME {
|
|
domain,
|
|
host: cname,
|
|
ttl,
|
|
})
|
|
}
|
|
QueryType::MX => {
|
|
let priority = buffer.read_u16()?;
|
|
let mut mx = String::new();
|
|
buffer.read_qname(&mut mx)?;
|
|
|
|
Ok(DnsRecord::MX {
|
|
domain,
|
|
priority,
|
|
host: mx,
|
|
ttl,
|
|
})
|
|
}
|
|
QueryType::UNKNOWN(_) => {
|
|
buffer.step(data_len as usize)?;
|
|
|
|
Ok(DnsRecord::UNKNOWN {
|
|
domain,
|
|
qtype: qtype_num,
|
|
data_len,
|
|
ttl,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
|
|
let start_pos = buffer.pos();
|
|
|
|
match *self {
|
|
DnsRecord::A {
|
|
ref domain,
|
|
ref addr,
|
|
ttl,
|
|
} => {
|
|
buffer.write_qname(domain)?;
|
|
buffer.write_u16(QueryType::A.to_num())?;
|
|
buffer.write_u16(1)?;
|
|
buffer.write_u32(ttl)?;
|
|
buffer.write_u16(4)?;
|
|
|
|
let octets = addr.octets();
|
|
buffer.write_u8(octets[0])?;
|
|
buffer.write_u8(octets[1])?;
|
|
buffer.write_u8(octets[2])?;
|
|
buffer.write_u8(octets[3])?;
|
|
}
|
|
DnsRecord::NS {
|
|
ref domain,
|
|
ref host,
|
|
ttl,
|
|
} => {
|
|
buffer.write_qname(domain)?;
|
|
buffer.write_u16(QueryType::NS.to_num())?;
|
|
buffer.write_u16(1)?;
|
|
buffer.write_u32(ttl)?;
|
|
|
|
let pos = buffer.pos();
|
|
buffer.write_u16(0)?;
|
|
buffer.write_qname(host)?;
|
|
|
|
let size = buffer.pos() - (pos + 2);
|
|
buffer.set_u16(pos, size as u16)?;
|
|
}
|
|
DnsRecord::CNAME {
|
|
ref domain,
|
|
ref host,
|
|
ttl,
|
|
} => {
|
|
buffer.write_qname(domain)?;
|
|
buffer.write_u16(QueryType::CNAME.to_num())?;
|
|
buffer.write_u16(1)?;
|
|
buffer.write_u32(ttl)?;
|
|
|
|
let pos = buffer.pos();
|
|
buffer.write_u16(0)?;
|
|
buffer.write_qname(host)?;
|
|
|
|
let size = buffer.pos() - (pos + 2);
|
|
buffer.set_u16(pos, size as u16)?;
|
|
}
|
|
DnsRecord::MX {
|
|
ref domain,
|
|
priority,
|
|
ref host,
|
|
ttl,
|
|
} => {
|
|
buffer.write_qname(domain)?;
|
|
buffer.write_u16(QueryType::MX.to_num())?;
|
|
buffer.write_u16(1)?;
|
|
buffer.write_u32(ttl)?;
|
|
|
|
let pos = buffer.pos();
|
|
buffer.write_u16(0)?;
|
|
buffer.write_u16(priority)?;
|
|
buffer.write_qname(host)?;
|
|
|
|
let size = buffer.pos() - (pos + 2);
|
|
buffer.set_u16(pos, size as u16)?;
|
|
}
|
|
DnsRecord::AAAA {
|
|
ref domain,
|
|
ref addr,
|
|
ttl,
|
|
} => {
|
|
buffer.write_qname(domain)?;
|
|
buffer.write_u16(QueryType::AAAA.to_num())?;
|
|
buffer.write_u16(1)?;
|
|
buffer.write_u32(ttl)?;
|
|
buffer.write_u16(16)?;
|
|
|
|
for octet in &addr.segments() {
|
|
buffer.write_u16(*octet)?;
|
|
}
|
|
}
|
|
DnsRecord::UNKNOWN { .. } => {
|
|
println!("Skipping record: {:?}", self);
|
|
}
|
|
}
|
|
|
|
Ok(buffer.pos() - start_pos)
|
|
}
|
|
}
|