Gave up on literate programming

This commit is contained in:
Emil Hernvall 2017-11-23 14:51:20 +01:00
parent 700334b7ad
commit 945d896208
15 changed files with 4254 additions and 222 deletions

109
Cargo.lock generated
View File

@ -1,109 +0,0 @@
[root]
name = "dnsguide"
version = "0.1.0"
dependencies = [
"tango 0.5.0 (git+https://github.com/pnkfelix/tango)",
]
[[package]]
name = "filetime"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "matches"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-serialize"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tango"
version = "0.5.0"
source = "git+https://github.com/pnkfelix/tango#9a673b5e74bf47e43c70fecabb5f10791d374e03"
dependencies = [
"filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-bidi"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-normalization"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "uuid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "walkdir"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"

View File

@ -1,12 +0,0 @@
[package]
name = "dnsguide"
version = "0.1.0"
authors = ["Emil Hernvall <emil@c0la.se>"]
build = "tango-build.rs"
[build-dependencies.tango]
git = "https://github.com/pnkfelix/tango"
[lib]
name = "lib"

View File

@ -1,9 +1,10 @@
Building a DNS server in Rust
=============================
To me, DNS is one the most exciting aspects of the Internet. Before it was
invented, everyone on the internet - which admittedly wasn't that many at that
stage - relied on a shared file called HOSTS.TXT, maintained by the Stanford
The internet has a rich conceptual foundation, with many exciting ideas that
enables it to function as we know it. One of the really cool ones is DNS. Before
it was invented, everyone on the internet - which admittedly wasn't that many at
that stage - relied on a shared file called HOSTS.TXT, maintained by the Stanford
Research Institute. This file was synchronized manually through FTP, and as the
number of hosts grew, so did the rate of change and the unfeasibility of the
system. In 1983, Paul Mockapetris set out to find a long term solution to the
@ -18,5 +19,8 @@ a truthful chronicle of that journey, but rather an idealized version of it,
without all the detours I ended up taking. We'll gradually implement a full
DNS server, starting from first principles.
* [Chapter 1 - The DNS protocol](/src/bin/chapter1.md)
* [Chapter 2 - Building a stub resolver](/src/bin/chapter2.md)
* [Chapter 1 - The DNS protocol](/chapter1.md)
* [Chapter 2 - Building a stub resolver](/chapter2.md)
* [Chapter 3 - Adding more Record Types](/chapter3.md)
* [Chapter 4 - Baby's first DNS server](/chapter4.md)
* [Chapter 5 - Recursive Resolve](/chapter5.md)

View File

@ -1,5 +1,5 @@
The DNS protocol
----------------
1. The DNS protocol
-------------------
We'll start out by investigating the DNS protocol and use our knowledge thereof
to implement a simple client.
@ -92,12 +92,12 @@ a lookup using the `dig` tool:
;; ANSWER SECTION:
google.com. 204 IN A 172.217.18.142
```
;; Query time: 0 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Wed Jul 06 13:24:19 CEST 2016
;; MSG SIZE rcvd: 44
```
We're using the `+noedns` flag to make sure we stick to the original format.
There are a few things of note in the output above:
@ -316,17 +316,9 @@ on to the record type.
### BytePacketBuffer
Now finally we know enough to start implementing! Some tools are needed:
```rust
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::fs::File;
```
The first order of business is that we need some convenient method for manipulating
the packets. For this, we'll use a `struct` called `BytePacketBuffer`.
Now finally we know enough to start implementing! The first order of business is
that we need some convenient method for manipulating the packets. For this,
we'll use a `struct` called `BytePacketBuffer`.
```rust
pub struct BytePacketBuffer {
@ -490,7 +482,7 @@ Read another byte, and calculate the jump offset:
Indicate that a jump was performed.
```rust
jumped = true;
jumped = true;
```
Restart the loop and retry at the new position.
@ -800,7 +792,7 @@ impl DnsRecord {
### DnsPacket
Finally, we're going to compose them in a struct called `DnsPacket`:
Finally, let's put it all together in a struct called `DnsPacket`:
```rust
#[derive(Clone, Debug)]

View File

@ -1,5 +1,5 @@
Building a stub resolver
------------------------
2. Building a stub resolver
---------------------------
While it's slightly satisfying to know that we're able to succesfully parse DNS
packets, it's not much use to just read them off disk. As our next step, we'll
@ -15,61 +15,49 @@ packets, but also write them. To do so, we'll need to extend `BytePacketBuffer`
with some additional methods:
```rust
extern crate dnsguide;
use dnsguide::chapter2::*;
```
impl BytePacketBuffer {
```rust
trait WriteableBuffer {
fn write(&mut self, val: u8) -> Result<usize>;
fn write_u8(&mut self, val: u8) -> Result<usize>;
fn write_u16(&mut self, val: u16) -> Result<usize>;
fn write_u32(&mut self, val: u32) -> Result<usize>;
fn write_qname(&mut self, qname: &str) -> Result<usize>;
}
- snip -
impl WriteableBuffer for BytePacketBuffer {
fn write(&mut self, val: u8) -> Result<usize> {
fn write(&mut self, val: u8) -> Result<()> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
self.buf[self.pos] = val;
self.pos += 1;
Ok(1)
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<usize> {
fn write_u8(&mut self, val: u8) -> Result<()> {
try!(self.write(val));
Ok(1)
Ok(())
}
fn write_u16(&mut self, val: u16) -> Result<usize> {
fn write_u16(&mut self, val: u16) -> Result<()> {
try!(self.write((val >> 8) as u8));
try!(self.write((val & 0xFF) as u8));
Ok(2)
Ok(())
}
fn write_u32(&mut self, val: u32) -> Result<usize> {
fn write_u32(&mut self, val: u32) -> Result<()> {
try!(self.write(((val >> 24) & 0xFF) as u8));
try!(self.write(((val >> 16) & 0xFF) as u8));
try!(self.write(((val >> 8) & 0xFF) as u8));
try!(self.write(((val >> 0) & 0xFF) as u8));
Ok(4)
Ok(())
}
```
We'll also need a function for writing query names in labeled form:
```rust
fn write_qname(&mut self, qname: &str) -> Result<usize> {
fn write_qname(&mut self, qname: &str) -> Result<()> {
let split_str = qname.split('.').collect::<Vec<&str>>();
let mut bytes_written = 0;
for label in split_str {
let len = label.len();
if len > 0x34 {
@ -77,18 +65,14 @@ We'll also need a function for writing query names in labeled form:
}
try!(self.write_u8(len as u8));
bytes_written += 1;
for b in label.as_bytes() {
try!(self.write_u8(*b));
bytes_written += 1;
}
}
try!(self.write_u8(0));
bytes_written += 1;
Ok(bytes_written)
Ok(())
}
} // End of BytePacketBuffer
@ -100,13 +84,11 @@ Building on our new functions we can extend our protocol representation
structs. Starting with `DnsHeader`:
```rust
trait BufferWriteable {
fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize>;
}
impl DnsHeader {
impl BufferWriteable for DnsHeader {
- snip -
fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_u16(self.id));
try!(buffer.write_u8( ((self.recursion_desired as u8)) |
@ -126,7 +108,7 @@ impl BufferWriteable for DnsHeader {
try!(buffer.write_u16(self.authoritative_entries));
try!(buffer.write_u16(self.resource_entries));
Ok(12)
Ok(())
}
}
@ -137,19 +119,19 @@ impl BufferWriteable for DnsHeader {
Moving on to `DnsQuestion`:
```rust
impl BufferWriteable for DnsQuestion {
impl DnsQuestion {
fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
- snip -
let mut bytes_written = 0;
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
bytes_written += try!(buffer.write_qname(&self.name));
try!(buffer.write_qname(&self.name));
let typenum = self.qtype.to_num();
bytes_written += try!(buffer.write_u16(typenum));
bytes_written += try!(buffer.write_u16(1));
try!(buffer.write_u16(typenum));
try!(buffer.write_u16(1));
Ok(bytes_written)
Ok(())
}
}
@ -161,9 +143,11 @@ impl BufferWriteable for DnsQuestion {
quite a bit of code here to handle different record types:
```rust
impl BufferWriteable for DnsRecord {
impl DnsRecord {
fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
- snip -
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
@ -194,38 +178,36 @@ impl BufferWriteable for DnsRecord {
### Extending DnsPacket for writing
Based on what we've accomplished, we can amend `DnsPacket` with its own
`write` function:
Putting it all together in `DnsPacket`:
```rust
impl BufferWriteable for DnsPacket {
impl DnsPacket {
fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize>
- snip -
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()>
{
let mut header = self.header.clone();
header.questions = self.questions.len() as u16;
header.answers = self.answers.len() as u16;
header.authoritative_entries = self.authorities.len() as u16;
header.resource_entries = self.resources.len() as u16;
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;
let mut bytes_written = 0;
bytes_written += try!(header.write(buffer));
try!(self.header.write(buffer));
for question in &self.questions {
bytes_written += try!(question.write(buffer));
try!(question.write(buffer));
}
for rec in &self.answers {
bytes_written += try!(rec.write(buffer));
try!(rec.write(buffer));
}
for rec in &self.authorities {
bytes_written += try!(rec.write(buffer));
try!(rec.write(buffer));
}
for rec in &self.resources {
bytes_written += try!(rec.write(buffer));
try!(rec.write(buffer));
}
Ok(bytes_written)
Ok(())
}
}
@ -249,6 +231,7 @@ fn main() {
let socket = UdpSocket::bind(("0.0.0.0", 43210)).unwrap();
```
Next we'll build our query packet. It's important that we remember to set the
`recursion_desired` flag. As noted earlier, the packet id is arbitrary.
@ -264,8 +247,8 @@ Next we'll build our query packet. It's important that we remember to set the
We can use our new write method to write the packet to a buffer...
```rust
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
```
...and send it off to the server using our socket:

472
chapter3.md Normal file
View File

@ -0,0 +1,472 @@
3. Adding more Record Types
---------------------------
Let's use our program to do a lookup for ''yahoo.com''.
```rust
let qname = "www.yahoo.com";
```
Running it yields:
```text
DnsHeader {
id: 6666,
recursion_desired: true,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: true,
rescode: NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: true,
questions: 1,
answers: 3,
authoritative_entries: 0,
resource_entries: 0
}
DnsQuestion {
name: "www.yahoo.com",
qtype: A
}
UNKNOWN {
domain: "www.yahoo.com",
qtype: 5,
data_len: 15,
ttl: 259
}
A {
domain: "fd-fp3.wg1.b.yahoo.com",
addr: 46.228.47.115,
ttl: 19
}
A {
domain: "fd-fp3.wg1.b.yahoo.com",
addr: 46.228.47.114,
ttl: 19
}
```
That's odd -- we're getting an UNKNOWN record as well as two A records. The
UNKNOWN record, with query type 5 is a CNAME. There are quite a few DNS record
types, many of which doesn't see any use in practice. That said, let's have
a look at a few essential ones:
| ID | Name | Description | Encoding |
| --- | ----- | -------------------------------------------------------- | ------------------------------------------------ |
| 1 | A | Alias - Mapping names to IP addresses | Preamble + Four bytes for IPv4 adress |
| 2 | NS | Name Server - The DNS server address for a domain | Preamble + Label Sequence |
| 5 | CNAME | Canonical Name - Maps names to names | Preamble + Label Sequence |
| 15 | MX | Mail eXchange - The host of the mail server for a domain | Preamble + 2-bytes for priority + Label Sequence |
| 28 | AAAA | IPv6 alias | Premable + Sixteen bytes for IPv6 adress |
### Extending QueryType with more record types
Let's go ahead and add them to our code! First we'll update our `QueryType`
enum:
```rust
#[derive(PartialEq,Eq,Debug,Clone,Hash,Copy)]
pub enum QueryType {
UNKNOWN(u16),
A, // 1
NS, // 2
CNAME, // 5
MX, // 15
AAAA, // 28
}
```
We'll also need to change our utility functions.
```rust
impl QueryType {
pub fn to_num(&self) -> u16 {
match *self {
QueryType::UNKNOWN(x) => x,
QueryType::A => 1,
QueryType::NS => 2,
QueryType::CNAME => 5,
QueryType::MX => 15,
QueryType::AAAA => 28,
}
}
pub fn from_num(num: u16) -> QueryType {
match num {
1 => QueryType::A,
2 => QueryType::NS,
5 => QueryType::CNAME,
15 => QueryType::MX,
28 => QueryType::AAAA,
_ => QueryType::UNKNOWN(num)
}
}
}
```
### Extending DnsRecord for reading new record types
Now we need a way of holding the data for these records, so we'll make some
modifications to `DnsRecord`.
```rust
#[derive(Debug,Clone,PartialEq,Eq,Hash,PartialOrd,Ord)]
#[allow(dead_code)]
pub enum DnsRecord {
UNKNOWN {
domain: String,
qtype: u16,
data_len: u16,
ttl: u32
}, // 0
A {
domain: String,
addr: Ipv4Addr,
ttl: u32
}, // 1
NS {
domain: String,
host: String,
ttl: u32
}, // 2
CNAME {
domain: String,
host: String,
ttl: u32
}, // 5
MX {
domain: String,
priority: u16,
host: String,
ttl: u32
}, // 15
AAAA {
domain: String,
addr: Ipv6Addr,
ttl: u32
}, // 28
}
```
Here comes the bulk of the work. We'll need to extend the functions for writing
and reading records. Starting with read, we amend it with additional code for
each record type. First off, we've got the common preamble:
```rust
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
let mut domain = String::new();
try!(buffer.read_qname(&mut domain));
let qtype_num = try!(buffer.read_u16());
let qtype = QueryType::from_num(qtype_num);
let _ = try!(buffer.read_u16());
let ttl = try!(buffer.read_u32());
let data_len = try!(buffer.read_u16());
```
After which we handle each record type separately, starting with the A record
type which remains the same as before.
```rust
match qtype {
QueryType::A => {
let raw_addr = try!(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 >> 0) & 0xFF) as u8);
Ok(DnsRecord::A {
domain: domain,
addr: addr,
ttl: ttl
})
},
```
The AAAA record type follows the same logic, but with more numbers to keep
track off.
```rust
QueryType::AAAA => {
let raw_addr1 = try!(buffer.read_u32());
let raw_addr2 = try!(buffer.read_u32());
let raw_addr3 = try!(buffer.read_u32());
let raw_addr4 = try!(buffer.read_u32());
let addr = Ipv6Addr::new(((raw_addr1 >> 16) & 0xFFFF) as u16,
((raw_addr1 >> 0) & 0xFFFF) as u16,
((raw_addr2 >> 16) & 0xFFFF) as u16,
((raw_addr2 >> 0) & 0xFFFF) as u16,
((raw_addr3 >> 16) & 0xFFFF) as u16,
((raw_addr3 >> 0) & 0xFFFF) as u16,
((raw_addr4 >> 16) & 0xFFFF) as u16,
((raw_addr4 >> 0) & 0xFFFF) as u16);
Ok(DnsRecord::AAAA {
domain: domain,
addr: addr,
ttl: ttl
})
},
```
NS and CNAME both have the same structure.
```rust
QueryType::NS => {
let mut ns = String::new();
try!(buffer.read_qname(&mut ns));
Ok(DnsRecord::NS {
domain: domain,
host: ns,
ttl: ttl
})
},
QueryType::CNAME => {
let mut cname = String::new();
try!(buffer.read_qname(&mut cname));
Ok(DnsRecord::CNAME {
domain: domain,
host: cname,
ttl: ttl
})
},
```
MX is close to the previous two, but with one extra field for priority.
```rust
QueryType::MX => {
let priority = try!(buffer.read_u16());
let mut mx = String::new();
try!(buffer.read_qname(&mut mx));
Ok(DnsRecord::MX {
domain: domain,
priority: priority,
host: mx,
ttl: ttl
})
},
```
And we end with some code for handling unknown record types, as before.
```rust
QueryType::UNKNOWN(_) => {
try!(buffer.step(data_len as usize));
Ok(DnsRecord::UNKNOWN {
domain: domain,
qtype: qtype_num,
data_len: data_len,
ttl: ttl
})
}
}
}
```
It's a bit of a mouthful, but individually not much more complex than what we
had.
### Extending BytePacketBuffer for setting values in place
Before we move on to writing records, we'll have to add two more functions to
`BytePacketBuffer`:
```rust
impl BytePacketBuffer {
- snip -
fn set(&mut self, pos: usize, val: u8) -> Result<()> {
self.buf[pos] = val;
Ok(())
}
fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
try!(self.set(pos,(val >> 8) as u8));
try!(self.set(pos+1,(val & 0xFF) as u8));
Ok(())
}
}
```
### Extending DnsRecord for writing new record types
Now we can amend `DnsRecord::write`. Here's our new function:
```rust
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
match *self {
DnsRecord::A { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::A.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(4));
let octets = addr.octets();
try!(buffer.write_u8(octets[0]));
try!(buffer.write_u8(octets[1]));
try!(buffer.write_u8(octets[2]));
try!(buffer.write_u8(octets[3]));
},
DnsRecord::NS { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::NS.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::CNAME { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::CNAME.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::MX { ref domain, priority, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::MX.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_u16(priority));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::AAAA { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::AAAA.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(16));
for octet in &addr.segments() {
try!(buffer.write_u16(*octet));
}
},
DnsRecord::UNKNOWN { .. } => {
println!("Skipping record: {:?}", self);
}
}
Ok(buffer.pos() - start_pos)
}
```
Again, quite a bit of extra code, but thankfully the last thing we've got to
do. We're still not using the write part, but it'll come in handy once we write
our server.
### Testing the new record types
Now we're ready to retry our ''yahoo.com'' query:
```text
DnsHeader {
id: 6666,
recursion_desired: true,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: true,
rescode: NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: true,
questions: 1,
answers: 3,
authoritative_entries: 0,
resource_entries: 0
}
DnsQuestion {
name: "www.yahoo.com",
qtype: A
}
CNAME {
domain: "www.yahoo.com",
host: "fd-fp3.wg1.b.yahoo.com",
ttl: 3
}
A {
domain: "fd-fp3.wg1.b.yahoo.com",
addr: 46.228.47.115,
ttl: 19
}
A {
domain: "fd-fp3.wg1.b.yahoo.com",
addr: 46.228.47.114,
ttl: 19
}
```
For good measure, let's try doing an MX lookup as well:
```rust
let qname = "yahoo.com";
let qtype = QueryType::MX;
```
Which yields:
```text
- snip -
DnsQuestion {
name: "yahoo.com",
qtype: MX
}
MX {
domain: "yahoo.com",
priority: 1,
host: "mta6.am0.yahoodns.net",
ttl: 1794
}
MX {
domain: "yahoo.com",
priority: 1,
host: "mta7.am0.yahoodns.net",
ttl: 1794
}
MX {
domain: "yahoo.com",
priority: 1,
host: "mta5.am0.yahoodns.net",
ttl: 1794
}
```
Encouraging!

352
chapter4.md Normal file
View File

@ -0,0 +1,352 @@
4. Baby's first DNS server
--------------------------
Haven gotten this far, we're ready to make our first attempt at writing an
actual server. In reality, DNS servers fullfil two different purposes:
* Authoritative Server - A DNS server hosting one or more "zones". For
instance, the authoritative servers for the zone google.com are
ns1.google.com, ns2.google.com, ns3.google.com and ns4.google.com.
* Caching Server - A DNS server that services DNS lookups by first checking
its cache to see if it already knows of the record being requested, and if
not performing a recursive lookup to figure it out. This includes the DNS
server that is likely running on your home router as well as the DNS server
that your ISP assigns to you through DHCP, and Google's public DNS servers
8.8.8.8 and 8.8.4.4.
Strictly speaking, there's nothing to stop a server from doing both things, but
in pracice these two roles are typically mutually exclusive. This also explains
the significance of the flags `RD` (Recursion Desired) and `RA` (Recursion
Available) in the packet header -- a stub resolver querying a caching server
will set the `RD` flag, and since the server allows such queries it will
perform the lookup and send a reply with the `RA` flag set. This won't work for
an Authoritative Server which will only reply to queries relating to the zones
hosted, and as such will send an error response to any queries with the `RD`
flag set.
Don't take my word for it, though! Let's verify that this is the case. First
off, let's use `8.8.8.8` for looking up *yahoo.com*:
```text
# dig @8.8.8.8 yahoo.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +recurse @8.8.8.8 yahoo.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53231
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;yahoo.com. IN A
;; ANSWER SECTION:
yahoo.com. 1051 IN A 98.138.253.109
yahoo.com. 1051 IN A 98.139.183.24
yahoo.com. 1051 IN A 206.190.36.45
;; Query time: 1 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Jul 08 11:43:55 CEST 2016
;; MSG SIZE rcvd: 86
```
This works as expected. Now let's try sending the same query to one of the
servers hosting the *google.com* zone:
```text
# dig @ns1.google.com yahoo.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +recurse @ns1.google.com yahoo.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 12034
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;yahoo.com. IN A
;; Query time: 10 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Fri Jul 08 11:44:07 CEST 2016
;; MSG SIZE rcvd: 27
```
Notice how the status of the response says `REFUSED`! `dig` also warns us that
while the `RD` flag was set in the query, the server didn't set it in the
response. We can still use the same server for *google.com*, however:
```text
dig @ns1.google.com google.com <<<
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +recurse @ns1.google.com google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28058
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 300 IN A 216.58.211.142
;; Query time: 10 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Fri Jul 08 11:46:27 CEST 2016
;; MSG SIZE rcvd: 44
```
No error this time -- however, `dig` still warns us that recursion is
unavailable. We can explicitly unset it using `+norecurse` which gets rid of
the warning:
```text
# dig +norecurse @ns1.google.com google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +norecurse @ns1.google.com google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15850
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 300 IN A 216.58.211.142
;; Query time: 10 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Fri Jul 08 11:47:52 CEST 2016
;; MSG SIZE rcvd: 44
```
This final query is the type of query that we'd expect to see a caching server
send as part of recursively resolving the name.
For our first foray into writing our own server, we'll do something even
simpler by implementing a server that simply forwards queries to another
caching server, i.e. a "DNS proxy server". Having already done most of the hard
work, it's a rather quick effort!
### Separating lookup into a separate function
We'll start out by doing some quick refactoring, moving our lookup code into
a separate function. This is for the most part the same code as we had in our
`main` function in the previous chapter, with the only change being that we
handle errors gracefully using `try!`.
```rust
fn lookup(qname: &str, qtype: QueryType, server: (&str, u16)) -> Result<DnsPacket> {
let socket = try!(UdpSocket::bind(("0.0.0.0", 43210)));
let mut packet = DnsPacket::new();
packet.header.id = 6666;
packet.header.questions = 1;
packet.header.recursion_desired = true;
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
try!(socket.send_to(&req_buffer.buf[0..req_buffer.pos], server));
let mut res_buffer = BytePacketBuffer::new();
socket.recv_from(&mut res_buffer.buf).unwrap();
DnsPacket::from_buffer(&mut res_buffer)
}
```
### Implementing our first server
Now we'll write our server code. First, we need get some things in order.
```rust
fn main() {
// Forward queries to Google's public DNS
let server = ("8.8.8.8", 53);
// Bind an UDP socket on port 2053
let socket = UdpSocket::bind(("0.0.0.0", 2053)).unwrap();
```
For now, queries are handled sequentially, so an infinite loop for servicing
requests is initiated.
```rust
loop {
```
With a socket ready, we can go ahead and read a packet. This will block until
one is received.
```rust
let mut req_buffer = BytePacketBuffer::new();
let (_, src) = match socket.recv_from(&mut req_buffer.buf) {
Ok(x) => x,
Err(e) => {
println!("Failed to read from UDP socket: {:?}", e);
continue;
}
};
```
Here we use match to safely unwrap the `Result`. If everything's as expected,
the raw bytes are simply returned, and if not it'll abort by restarting the
loop and waiting for the next request. The `recv_from` function will write the
data into the provided buffer, and return the length of the data read as well
as the source adress. We're not interested in the length, but we need to keep
track of the source in order to send our reply later on.
Next, `DnsPacket::from_buffer` is used to parse the raw bytes into
a `DnsPacket`. It uses the same error handling idiom as the previous statement.
```rust
let request = match DnsPacket::from_buffer(&mut req_buffer) {
Ok(x) => x,
Err(e) => {
println!("Failed to parse UDP query packet: {:?}", e);
continue;
}
};
```
At this stage, the response packet is created and initiated.
```rust
let mut packet = DnsPacket::new();
packet.header.id = request.header.id;
packet.header.recursion_desired = true;
packet.header.recursion_available = true;
packet.header.response = true;
```
Being mindful of how unreliable input data from arbitrary senders can be, we
need make sure that a question is actually present. If not, we return `FORMERR`
to indicate that the sender made something wrong.
```rust
if request.questions.is_empty() {
packet.header.rescode = ResultCode::FORMERR;
}
```
Usually a question will be present, though.
```rust
else {
let question = &request.questions[0];
println!("Received query: {:?}", question);
```
Since all is set up and as expected, the query can be forwarded to the target
server. There's always the possibility that the query will fail, in which case
the `SERVFAIL` response code is set to indicate as much to the client. If
rather everything goes as planned, the question and response records as copied
into our response packet.
```rust
if let Ok(result) = lookup(&question.name, question.qtype, server) {
packet.questions.push(question.clone());
packet.header.rescode = result.header.rescode;
for rec in result.answers {
println!("Answer: {:?}", rec);
packet.answers.push(rec);
}
for rec in result.authorities {
println!("Authority: {:?}", rec);
packet.authorities.push(rec);
}
for rec in result.resources {
println!("Resource: {:?}", rec);
packet.resources.push(rec);
}
} else {
packet.header.rescode = ResultCode::SERVFAIL;
}
```
The only thing remaining is to encode our response and send it off!
```rust
let mut res_buffer = BytePacketBuffer::new();
match packet.write(&mut res_buffer) {
Ok(_) => {},
Err(e) => {
println!("Failed to encode UDP response packet: {:?}", e);
continue;
}
};
let len = res_buffer.pos();
let data = match res_buffer.get_range(0, len) {
Ok(x) => x,
Err(e) => {
println!("Failed to retrieve response buffer: {:?}", e);
continue;
}
};
match socket.send_to(data, src) {
Ok(_) => {},
Err(e) => {
println!("Failed to send response buffer: {:?}", e);
continue;
}
};
```
The match idiom for error handling is used again here, since we want to avoid
terminating our request loop at all cost. It's a bit verbose, and normally we'd
like to use `try!` instead. Unfortunately that's unavailable to us here, since
we're in the `main` function which doesn't return a `Result`.
```rust
} // End of request loop
} // End of main
```
All done! Let's try it! We start our server in one terminal, and use `dig` to
perform a lookup in a second terminal.
```text
# dig @127.0.0.1 -p 2053 google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @127.0.0.1 -p 2053 google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47200
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 68 IN A 216.58.211.142
;; Query time: 1 msec
;; SERVER: 127.0.0.1#2053(127.0.0.1)
;; WHEN: Fri Jul 08 12:07:44 CEST 2016
;; MSG SIZE rcvd: 54
```
Looking at our server terminal we see:
```text
Received query: DnsQuestion { name: "google.com", qtype: A }
Answer: A { domain: "google.com", addr: 216.58.211.142, ttl: 96 }
```
In less than 800 lines of code, we've built a DNS server able to respond to
queries with several different record types!

427
chapter5.md Normal file
View File

@ -0,0 +1,427 @@
5. Recursive Resolve
--------------------
Our server is working, but being reliant on another server to actually perform
the lookup is annoying and less than useful. Now is a good time to dwelve into
the details of how a name is really resolved.
Assuming that no information is known since before, the question is first
issued to one of the Internet's 13 root servers. Why 13? Because that's how
many that fits into a 512 byte DNS packet (strictly speaking, there's room for
14, but some margin was left). You might think that 13 seems a bit on the low
side for handling all of the internet, and you'd be right -- there are 13
logical servers, but in reality many more. You can read more about it
[here](http://www.root-servers.org/). Any resolver will need to know of these
13 servers before hand. A file containing all of them, in bind format, is
available and called [named.root](https://www.internic.net/domain/named.root).
These servers all contain the same information, and to get started we can pick
one of them at random. Looking at `named.root` we see that the IP-adress of
*a.root-servers.net* is 198.41.0.4, so we'll go ahead and use that to perform
our initial query for *www.google.com*.
```text
# dig +norecurse @198.41.0.4 www.google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +norecurse @198.41.0.4 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64866
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 16
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com. IN A
;; AUTHORITY SECTION:
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
;; ADDITIONAL SECTION:
e.gtld-servers.net. 172800 IN A 192.12.94.30
b.gtld-servers.net. 172800 IN A 192.33.14.30
b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30
j.gtld-servers.net. 172800 IN A 192.48.79.30
m.gtld-servers.net. 172800 IN A 192.55.83.30
i.gtld-servers.net. 172800 IN A 192.43.172.30
f.gtld-servers.net. 172800 IN A 192.35.51.30
a.gtld-servers.net. 172800 IN A 192.5.6.30
a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30
g.gtld-servers.net. 172800 IN A 192.42.93.30
h.gtld-servers.net. 172800 IN A 192.54.112.30
l.gtld-servers.net. 172800 IN A 192.41.162.30
k.gtld-servers.net. 172800 IN A 192.52.178.30
c.gtld-servers.net. 172800 IN A 192.26.92.30
d.gtld-servers.net. 172800 IN A 192.31.80.30
;; Query time: 24 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Fri Jul 08 14:09:20 CEST 2016
;; MSG SIZE rcvd: 531
```
The root servers don't know about *www.google.com*, but they do know about
*com*, so our reply tells us where to go next. There are a few things to take
note of:
* We are provided with a set of NS records, which are in the authority
section. NS records tells us *the name* of the name server handling
a domain.
* The server is being helpful by passing along A records corresponding to the
NS records, so we don't have to perform a second lookup.
* We didn't actually perform a query for *com*, but rather *www.google.com*.
However, the NS records all refer to *com*.
Let's pick a server from the result and move on. *192.5.6.30* for
*a.gtld-servers.net* seems as good as any.
```text
# dig +norecurse @192.5.6.30 www.google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +norecurse @192.5.6.30 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16229
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 5
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com. IN A
;; AUTHORITY SECTION:
google.com. 172800 IN NS ns2.google.com.
google.com. 172800 IN NS ns1.google.com.
google.com. 172800 IN NS ns3.google.com.
google.com. 172800 IN NS ns4.google.com.
;; ADDITIONAL SECTION:
ns2.google.com. 172800 IN A 216.239.34.10
ns1.google.com. 172800 IN A 216.239.32.10
ns3.google.com. 172800 IN A 216.239.36.10
ns4.google.com. 172800 IN A 216.239.38.10
;; Query time: 114 msec
;; SERVER: 192.5.6.30#53(192.5.6.30)
;; WHEN: Fri Jul 08 14:13:26 CEST 2016
;; MSG SIZE rcvd: 179
```
We're still not at *www.google.com*, but at least we have a set of servers that
handle the *google.com* domain now. Let's give it another shot by sending our
query to *216.239.32.10*.
```text
# dig +norecurse @216.239.32.10 www.google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> +norecurse @216.239.32.10 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20432
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.google.com. IN A
;; ANSWER SECTION:
www.google.com. 300 IN A 216.58.211.132
;; Query time: 10 msec
;; SERVER: 216.239.32.10#53(216.239.32.10)
;; WHEN: Fri Jul 08 14:15:11 CEST 2016
;; MSG SIZE rcvd: 48
```
And here we go! The IP of *www.google.com* as we desired. Let's recap:
* *a.root-servers.net* tells us to check *a.gtld-servers.net* which handles com
* *a.gtld-servers.net* tells us to check *ns1.google.com* which handles google.com
* *ns1.google.com* tells us the IP of *www.google.com*
This is rather typical, and most lookups will only ever require three steps,
even without caching. It's still possible to have name servers for subdomains,
and further ones for sub-subdomains, though. In practice, a DNS server will
maintain a cache, and most TLD's will be known since before. That means that
most queries will only ever require two lookups by the server, and commonly one
or zero.
### Extending DnsPacket for recursive lookups
Before we can get on, we'll need a few utility functions on `DnsPacket`.
```rust
impl DnsPacket {
- snip -
```
First, it's useful to be able to pick a random A record from a packet. Since we
don't want to introduce an external dependency, and there's no method for
generating random numbers in the rust standard library, we'll just pick the
first entry for now.
```rust
pub fn get_random_a(&self) -> Option<String> {
if !self.answers.is_empty() {
let idx = random::<usize>() % self.answers.len();
let a_record = &self.answers[idx];
if let DnsRecord::A{ ref addr, .. } = *a_record {
return Some(addr.to_string());
}
}
None
}
```
Second, we'll use the fact that name servers often bundle the corresponding
A records when replying to an NS query to implement a function that returns
the actual IP for an NS record if possible.
```rust
pub fn get_resolved_ns(&self, qname: &str) -> Option<String> {
```
First, we scan the list of NS records in the authorities section:
```rust
let mut new_authorities = Vec::new();
for auth in &self.authorities {
if let DnsRecord::NS { ref domain, ref host, .. } = *auth {
if !qname.ends_with(domain) {
continue;
}
```
Once we've found an NS record, we scan the resources record for a matching
A record...
```rust
for rsrc in &self.resources {
if let DnsRecord::A{ ref domain, ref addr, ttl } = *rsrc {
if domain != host {
continue;
}
let rec = DnsRecord::A {
domain: host.clone(),
addr: *addr,
ttl: ttl
};
```
...and push any matches to a list.
```rust
new_authorities.push(rec);
}
}
}
}
```
If there are any matches, we pick the first one. Again, we'll want to introduce
randomization later on.
```rust
if !new_authorities.is_empty() {
if let DnsRecord::A { addr, .. } = new_authorities[0] {
return Some(addr.to_string());
}
}
None
} // End of get_resolved_ns
```
However, not all name servers are as well behaved. 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.
```rust
pub fn get_unresolved_ns(&self, qname: &str) -> Option<String> {
let mut new_authorities = Vec::new();
for auth in &self.authorities {
if let DnsRecord::NS { ref domain, ref host, .. } = *auth {
if !qname.ends_with(domain) {
continue;
}
new_authorities.push(host);
}
}
if !new_authorities.is_empty() {
let idx = random::<usize>() % new_authorities.len();
return Some(new_authorities[idx].clone());
}
None
} // End of get_unresolved_ns
} // End of DnsPacket
```
### Implementing recursive lookup
We move swiftly on to our new `recursive_lookup` function:
```rust
fn recursive_lookup(qname: &str, qtype: QueryType) -> Result<DnsPacket> {
```
For now we're always starting with *a.root-servers.net*.
```rust
let mut ns = "198.41.0.4".to_string();
```
Since it might take an arbitrary number of steps, we enter an unbounded loop.
```rust
loop {
println!("attempting lookup of {:?} {} with ns {}", qtype, qname, ns);
```
The next step is to send the query to the active server.
```rust
let ns_copy = ns.clone();
let server = (ns_copy.as_str(), 53);
let response = try!(lookup(qname, qtype.clone(), server));
```
If there are entries in the answer section, and no errors, we are done!
```rust
if !response.answers.is_empty() &&
response.header.rescode == ResultCode::NOERROR {
return Ok(response.clone());
}
```
We might also get a `NXDOMAIN` reply, which is the authoritative name servers
way of telling us that the name doesn't exist.
```rust
if response.header.rescode == ResultCode::NXDOMAIN {
return Ok(response.clone());
}
```
Otherwise, we'll try to find a new nameserver based on NS and a corresponding A
record in the additional section. If this succeeds, we can switch name server
and retry the loop.
```rust
if let Some(new_ns) = response.get_resolved_ns(qname) {
ns = new_ns.clone();
continue;
}
```
If not, we'll have to resolve the ip of a NS record. If no NS records exist,
we'll go with what the last server told us.
```rust
let new_ns_name = match response.get_unresolved_ns(qname) {
Some(x) => x,
None => return Ok(response.clone())
};
```
Here we go down the rabbit hole by starting _another_ lookup sequence in the
midst of our current one. Hopefully, this will give us the IP of an appropriate
name server.
```rust
let recursive_response = try!(recursive_lookup(&new_ns_name, QueryType::A));
```
Finally, we pick a random ip from the result, and restart the loop. If no such
record is available, we again return the last result we got.
```rust
if let Some(new_ns) = recursive_response.get_random_a() {
ns = new_ns.clone();
} else {
return Ok(response.clone())
}
}
} // End of recursive_lookup
```
### Trying out recursive lookup
The only thing remaining is to change our main function to use
`recursive_lookup`:
```rust
fn main() {
- snip -
println!("Received query: {:?}", question);
if let Ok(result) = recursive_lookup(&question.name, question.qtype) {
packet.questions.push(question.clone());
packet.header.rescode = result.header.rescode;
- snip -
}
```
Let's try it!
```text
# dig @127.0.0.1 -p 2053 www.google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @127.0.0.1 -p 2053 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41892
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.google.com. IN A
;; ANSWER SECTION:
www.google.com. 300 IN A 216.58.211.132
;; Query time: 76 msec
;; SERVER: 127.0.0.1#2053(127.0.0.1)
;; WHEN: Fri Jul 08 14:31:39 CEST 2016
;; MSG SIZE rcvd: 62
```
Looking at our server window, we see:
```text
Received query: DnsQuestion { name: "www.google.com", qtype: A }
attempting lookup of A www.google.com with ns 198.41.0.4
attempting lookup of A www.google.com with ns 192.12.94.30
attempting lookup of A www.google.com with ns 216.239.34.10
Answer: A { domain: "www.google.com", addr: 216.58.211.132, ttl: 300 }
```
This mirrors our manual process earlier. We're really getting somewhere!

View File

@ -1,7 +1,7 @@
pub use std::io::{Result, Read};
pub use std::io::{Error, ErrorKind};
pub use std::net::Ipv4Addr;
pub use std::net::UdpSocket;
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::fs::File;
pub struct BytePacketBuffer {
pub buf: [u8; 512],
@ -16,23 +16,23 @@ impl BytePacketBuffer {
}
}
pub fn pos(&self) -> usize {
fn pos(&self) -> usize {
self.pos
}
pub fn step(&mut self, steps: usize) -> Result<()> {
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
pub fn seek(&mut self, pos: usize) -> Result<()> {
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
pub fn read(&mut self) -> Result<u8> {
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
@ -42,21 +42,21 @@ impl BytePacketBuffer {
Ok(res)
}
pub fn get(&mut self, pos: usize) -> Result<u8> {
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(self.buf[pos])
}
pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(&self.buf[start..start+len as usize])
}
pub fn read_u16(&mut self) -> Result<u16>
fn read_u16(&mut self) -> Result<u16>
{
let res = ((try!(self.read()) as u16) << 8) |
(try!(self.read()) as u16);
@ -64,7 +64,7 @@ impl BytePacketBuffer {
Ok(res)
}
pub fn read_u32(&mut self) -> Result<u32>
fn read_u32(&mut self) -> Result<u32>
{
let res = ((try!(self.read()) as u32) << 24) |
((try!(self.read()) as u32) << 16) |
@ -74,7 +74,7 @@ impl BytePacketBuffer {
Ok(res)
}
pub fn read_qname(&mut self, outstr: &mut String) -> Result<()>
fn read_qname(&mut self, outstr: &mut String) -> Result<()>
{
let mut pos = self.pos();
let mut jumped = false;
@ -370,3 +370,25 @@ impl DnsPacket {
Ok(result)
}
}
fn main() {
let mut f = File::open("response_packet.txt").unwrap();
let mut buffer = BytePacketBuffer::new();
f.read(&mut buffer.buf).unwrap();
let packet = DnsPacket::from_buffer(&mut buffer).unwrap();
println!("{:?}", packet.header);
for q in packet.questions {
println!("{:?}", q);
}
for rec in packet.answers {
println!("{:?}", rec);
}
for rec in packet.authorities {
println!("{:?}", rec);
}
for rec in packet.resources {
println!("{:?}", rec);
}
}

550
samples/sample2.rs Normal file
View File

@ -0,0 +1,550 @@
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::net::UdpSocket;
pub struct BytePacketBuffer {
pub buf: [u8; 512],
pub pos: usize
}
impl BytePacketBuffer {
pub fn new() -> BytePacketBuffer {
BytePacketBuffer {
buf: [0; 512],
pos: 0
}
}
fn pos(&self) -> usize {
self.pos
}
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
let res = self.buf[self.pos];
self.pos += 1;
Ok(res)
}
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(self.buf[pos])
}
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(&self.buf[start..start+len as usize])
}
fn read_u16(&mut self) -> Result<u16>
{
let res = ((try!(self.read()) as u16) << 8) |
(try!(self.read()) as u16);
Ok(res)
}
fn read_u32(&mut self) -> Result<u32>
{
let res = ((try!(self.read()) as u32) << 24) |
((try!(self.read()) as u32) << 16) |
((try!(self.read()) as u32) << 8) |
((try!(self.read()) as u32) << 0);
Ok(res)
}
fn read_qname(&mut self, outstr: &mut String) -> Result<()>
{
let mut pos = self.pos();
let mut jumped = false;
let mut delim = "";
loop {
let len = try!(self.get(pos));
// A two byte sequence, where the two highest bits of the first byte is
// set, represents a offset relative to the start of the buffer. We
// handle this by jumping to the offset, setting a flag to indicate
// that we shouldn't update the shared buffer position once done.
if (len & 0xC0) > 0 {
// When a jump is performed, we only modify the shared buffer
// position once, and avoid making the change later on.
if !jumped {
try!(self.seek(pos+2));
}
let b2 = try!(self.get(pos+1)) as u16;
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
pos = offset as usize;
jumped = true;
continue;
}
pos += 1;
// Names are terminated by an empty label of length 0
if len == 0 {
break;
}
outstr.push_str(delim);
let str_buffer = try!(self.get_range(pos, len as usize));
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
pos += len as usize;
}
if !jumped {
try!(self.seek(pos));
}
Ok(())
}
fn write(&mut self, val: u8) -> Result<()> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
self.buf[self.pos] = val;
self.pos += 1;
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<()> {
try!(self.write(val));
Ok(())
}
fn write_u16(&mut self, val: u16) -> Result<()> {
try!(self.write((val >> 8) as u8));
try!(self.write((val & 0xFF) as u8));
Ok(())
}
fn write_u32(&mut self, val: u32) -> Result<()> {
try!(self.write(((val >> 24) & 0xFF) as u8));
try!(self.write(((val >> 16) & 0xFF) as u8));
try!(self.write(((val >> 8) & 0xFF) as u8));
try!(self.write(((val >> 0) & 0xFF) as u8));
Ok(())
}
fn write_qname(&mut self, qname: &str) -> Result<()> {
let split_str = qname.split('.').collect::<Vec<&str>>();
for label in split_str {
let len = label.len();
if len > 0x34 {
return Err(Error::new(ErrorKind::InvalidInput, "Single label exceeds 63 characters of length"));
}
try!(self.write_u8(len as u8));
for b in label.as_bytes() {
try!(self.write_u8(*b));
}
}
try!(self.write_u8(0));
Ok(())
}
}
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum ResultCode {
NOERROR = 0,
FORMERR = 1,
SERVFAIL = 2,
NXDOMAIN = 3,
NOTIMP = 4,
REFUSED = 5
}
impl ResultCode {
pub fn from_num(num: u8) -> ResultCode {
match num {
1 => ResultCode::FORMERR,
2 => ResultCode::SERVFAIL,
3 => ResultCode::NXDOMAIN,
4 => ResultCode::NOTIMP,
5 => ResultCode::REFUSED,
0 | _ => ResultCode::NOERROR
}
}
}
#[derive(Clone,Debug)]
pub struct DnsHeader {
pub id: u16, // 16 bits
pub recursion_desired: bool, // 1 bit
pub truncated_message: bool, // 1 bit
pub authoritative_answer: bool, // 1 bit
pub opcode: u8, // 4 bits
pub response: bool, // 1 bit
pub rescode: ResultCode, // 4 bits
pub checking_disabled: bool, // 1 bit
pub authed_data: bool, // 1 bit
pub z: bool, // 1 bit
pub recursion_available: bool, // 1 bit
pub questions: u16, // 16 bits
pub answers: u16, // 16 bits
pub authoritative_entries: u16, // 16 bits
pub resource_entries: u16 // 16 bits
}
impl DnsHeader {
pub fn new() -> DnsHeader {
DnsHeader { id: 0,
recursion_desired: false,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: false,
rescode: ResultCode::NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: false,
questions: 0,
answers: 0,
authoritative_entries: 0,
resource_entries: 0 }
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
self.id = try!(buffer.read_u16());
let flags = try!(buffer.read_u16());
let a = (flags >> 8) as u8;
let b = (flags & 0xFF) as u8;
self.recursion_desired = (a & (1 << 0)) > 0;
self.truncated_message = (a & (1 << 1)) > 0;
self.authoritative_answer = (a & (1 << 2)) > 0;
self.opcode = (a >> 3) & 0x0F;
self.response = (a & (1 << 7)) > 0;
self.rescode = ResultCode::from_num(b & 0x0F);
self.checking_disabled = (b & (1 << 4)) > 0;
self.authed_data = (b & (1 << 5)) > 0;
self.z = (b & (1 << 6)) > 0;
self.recursion_available = (b & (1 << 7)) > 0;
self.questions = try!(buffer.read_u16());
self.answers = try!(buffer.read_u16());
self.authoritative_entries = try!(buffer.read_u16());
self.resource_entries = try!(buffer.read_u16());
// Return the constant header size
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_u16(self.id));
try!(buffer.write_u8( ((self.recursion_desired as u8)) |
((self.truncated_message as u8) << 1) |
((self.authoritative_answer as u8) << 2) |
(self.opcode << 3) |
((self.response as u8) << 7) as u8) );
try!(buffer.write_u8( (self.rescode.clone() as u8) |
((self.checking_disabled as u8) << 4) |
((self.authed_data as u8) << 5) |
((self.z as u8) << 6) |
((self.recursion_available as u8) << 7) ));
try!(buffer.write_u16(self.questions));
try!(buffer.write_u16(self.answers));
try!(buffer.write_u16(self.authoritative_entries));
try!(buffer.write_u16(self.resource_entries));
Ok(())
}
}
#[derive(PartialEq,Eq,Debug,Clone,Hash,Copy)]
pub enum QueryType {
UNKNOWN(u16),
A, // 1
}
impl QueryType {
pub fn to_num(&self) -> u16 {
match *self {
QueryType::UNKNOWN(x) => x,
QueryType::A => 1,
}
}
pub fn from_num(num: u16) -> QueryType {
match num {
1 => QueryType::A,
_ => QueryType::UNKNOWN(num)
}
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub struct DnsQuestion {
pub name: String,
pub qtype: QueryType
}
impl DnsQuestion {
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
DnsQuestion {
name: name,
qtype: qtype
}
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.read_qname(&mut self.name));
self.qtype = QueryType::from_num(try!(buffer.read_u16())); // qtype
let _ = try!(buffer.read_u16()); // class
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_qname(&self.name));
let typenum = self.qtype.to_num();
try!(buffer.write_u16(typenum));
try!(buffer.write_u16(1));
Ok(())
}
}
#[derive(Debug,Clone,PartialEq,Eq,Hash,PartialOrd,Ord)]
#[allow(dead_code)]
pub enum DnsRecord {
UNKNOWN {
domain: String,
qtype: u16,
data_len: u16,
ttl: u32
}, // 0
A {
domain: String,
addr: Ipv4Addr,
ttl: u32
}, // 1
}
impl DnsRecord {
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
let mut domain = String::new();
try!(buffer.read_qname(&mut domain));
let qtype_num = try!(buffer.read_u16());
let qtype = QueryType::from_num(qtype_num);
let _ = try!(buffer.read_u16());
let ttl = try!(buffer.read_u32());
let data_len = try!(buffer.read_u16());
match qtype {
QueryType::A => {
let raw_addr = try!(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 >> 0) & 0xFF) as u8);
Ok(DnsRecord::A {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::UNKNOWN(_) => {
try!(buffer.step(data_len as usize));
Ok(DnsRecord::UNKNOWN {
domain: domain,
qtype: qtype_num,
data_len: data_len,
ttl: ttl
})
}
}
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
match *self {
DnsRecord::A { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::A.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(4));
let octets = addr.octets();
try!(buffer.write_u8(octets[0]));
try!(buffer.write_u8(octets[1]));
try!(buffer.write_u8(octets[2]));
try!(buffer.write_u8(octets[3]));
},
DnsRecord::UNKNOWN { .. } => {
println!("Skipping record: {:?}", self);
}
}
Ok(buffer.pos() - start_pos)
}
}
#[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> {
let mut result = DnsPacket::new();
try!(result.header.read(buffer));
for _ in 0..result.header.questions {
let mut question = DnsQuestion::new("".to_string(),
QueryType::UNKNOWN(0));
try!(question.read(buffer));
result.questions.push(question);
}
for _ in 0..result.header.answers {
let rec = try!(DnsRecord::read(buffer));
result.answers.push(rec);
}
for _ in 0..result.header.authoritative_entries {
let rec = try!(DnsRecord::read(buffer));
result.authorities.push(rec);
}
for _ in 0..result.header.resource_entries {
let rec = try!(DnsRecord::read(buffer));
result.resources.push(rec);
}
Ok(result)
}
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()>
{
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;
try!(self.header.write(buffer));
for question in &self.questions {
try!(question.write(buffer));
}
for rec in &self.answers {
try!(rec.write(buffer));
}
for rec in &self.authorities {
try!(rec.write(buffer));
}
for rec in &self.resources {
try!(rec.write(buffer));
}
Ok(())
}
}
fn main() {
let qname = "www.yahoo.com";
let qtype = QueryType::A;
let server = ("8.8.8.8", 53);
let socket = UdpSocket::bind(("0.0.0.0", 43210)).unwrap();
let mut packet = DnsPacket::new();
packet.header.id = 6666;
packet.header.questions = 1;
packet.header.recursion_desired = true;
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
socket.send_to(&req_buffer.buf[0..req_buffer.pos], server).unwrap();
let mut res_buffer = BytePacketBuffer::new();
socket.recv_from(&mut res_buffer.buf).unwrap();
let res_packet = DnsPacket::from_buffer(&mut res_buffer).unwrap();
println!("{:?}", res_packet.header);
for q in res_packet.questions {
println!("{:?}", q);
}
for rec in res_packet.answers {
println!("{:?}", rec);
}
for rec in res_packet.authorities {
println!("{:?}", rec);
}
for rec in res_packet.resources {
println!("{:?}", rec);
}
}

701
samples/sample3.rs Normal file
View File

@ -0,0 +1,701 @@
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::{Ipv4Addr,Ipv6Addr};
use std::net::UdpSocket;
pub struct BytePacketBuffer {
pub buf: [u8; 512],
pub pos: usize
}
impl BytePacketBuffer {
pub fn new() -> BytePacketBuffer {
BytePacketBuffer {
buf: [0; 512],
pos: 0
}
}
fn pos(&self) -> usize {
self.pos
}
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
let res = self.buf[self.pos];
self.pos += 1;
Ok(res)
}
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(self.buf[pos])
}
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(&self.buf[start..start+len as usize])
}
fn read_u16(&mut self) -> Result<u16>
{
let res = ((try!(self.read()) as u16) << 8) |
(try!(self.read()) as u16);
Ok(res)
}
fn read_u32(&mut self) -> Result<u32>
{
let res = ((try!(self.read()) as u32) << 24) |
((try!(self.read()) as u32) << 16) |
((try!(self.read()) as u32) << 8) |
((try!(self.read()) as u32) << 0);
Ok(res)
}
fn read_qname(&mut self, outstr: &mut String) -> Result<()>
{
let mut pos = self.pos();
let mut jumped = false;
let mut delim = "";
loop {
let len = try!(self.get(pos));
// A two byte sequence, where the two highest bits of the first byte is
// set, represents a offset relative to the start of the buffer. We
// handle this by jumping to the offset, setting a flag to indicate
// that we shouldn't update the shared buffer position once done.
if (len & 0xC0) > 0 {
// When a jump is performed, we only modify the shared buffer
// position once, and avoid making the change later on.
if !jumped {
try!(self.seek(pos+2));
}
let b2 = try!(self.get(pos+1)) as u16;
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
pos = offset as usize;
jumped = true;
continue;
}
pos += 1;
// Names are terminated by an empty label of length 0
if len == 0 {
break;
}
outstr.push_str(delim);
let str_buffer = try!(self.get_range(pos, len as usize));
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
pos += len as usize;
}
if !jumped {
try!(self.seek(pos));
}
Ok(())
}
fn write(&mut self, val: u8) -> Result<()> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
self.buf[self.pos] = val;
self.pos += 1;
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<()> {
try!(self.write(val));
Ok(())
}
fn write_u16(&mut self, val: u16) -> Result<()> {
try!(self.write((val >> 8) as u8));
try!(self.write((val & 0xFF) as u8));
Ok(())
}
fn write_u32(&mut self, val: u32) -> Result<()> {
try!(self.write(((val >> 24) & 0xFF) as u8));
try!(self.write(((val >> 16) & 0xFF) as u8));
try!(self.write(((val >> 8) & 0xFF) as u8));
try!(self.write(((val >> 0) & 0xFF) as u8));
Ok(())
}
fn write_qname(&mut self, qname: &str) -> Result<()> {
let split_str = qname.split('.').collect::<Vec<&str>>();
for label in split_str {
let len = label.len();
if len > 0x34 {
return Err(Error::new(ErrorKind::InvalidInput, "Single label exceeds 63 characters of length"));
}
try!(self.write_u8(len as u8));
for b in label.as_bytes() {
try!(self.write_u8(*b));
}
}
try!(self.write_u8(0));
Ok(())
}
fn set(&mut self, pos: usize, val: u8) -> Result<()> {
self.buf[pos] = val;
Ok(())
}
fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
try!(self.set(pos,(val >> 8) as u8));
try!(self.set(pos+1,(val & 0xFF) as u8));
Ok(())
}
}
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum ResultCode {
NOERROR = 0,
FORMERR = 1,
SERVFAIL = 2,
NXDOMAIN = 3,
NOTIMP = 4,
REFUSED = 5
}
impl ResultCode {
pub fn from_num(num: u8) -> ResultCode {
match num {
1 => ResultCode::FORMERR,
2 => ResultCode::SERVFAIL,
3 => ResultCode::NXDOMAIN,
4 => ResultCode::NOTIMP,
5 => ResultCode::REFUSED,
0 | _ => ResultCode::NOERROR
}
}
}
#[derive(Clone,Debug)]
pub struct DnsHeader {
pub id: u16, // 16 bits
pub recursion_desired: bool, // 1 bit
pub truncated_message: bool, // 1 bit
pub authoritative_answer: bool, // 1 bit
pub opcode: u8, // 4 bits
pub response: bool, // 1 bit
pub rescode: ResultCode, // 4 bits
pub checking_disabled: bool, // 1 bit
pub authed_data: bool, // 1 bit
pub z: bool, // 1 bit
pub recursion_available: bool, // 1 bit
pub questions: u16, // 16 bits
pub answers: u16, // 16 bits
pub authoritative_entries: u16, // 16 bits
pub resource_entries: u16 // 16 bits
}
impl DnsHeader {
pub fn new() -> DnsHeader {
DnsHeader { id: 0,
recursion_desired: false,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: false,
rescode: ResultCode::NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: false,
questions: 0,
answers: 0,
authoritative_entries: 0,
resource_entries: 0 }
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
self.id = try!(buffer.read_u16());
let flags = try!(buffer.read_u16());
let a = (flags >> 8) as u8;
let b = (flags & 0xFF) as u8;
self.recursion_desired = (a & (1 << 0)) > 0;
self.truncated_message = (a & (1 << 1)) > 0;
self.authoritative_answer = (a & (1 << 2)) > 0;
self.opcode = (a >> 3) & 0x0F;
self.response = (a & (1 << 7)) > 0;
self.rescode = ResultCode::from_num(b & 0x0F);
self.checking_disabled = (b & (1 << 4)) > 0;
self.authed_data = (b & (1 << 5)) > 0;
self.z = (b & (1 << 6)) > 0;
self.recursion_available = (b & (1 << 7)) > 0;
self.questions = try!(buffer.read_u16());
self.answers = try!(buffer.read_u16());
self.authoritative_entries = try!(buffer.read_u16());
self.resource_entries = try!(buffer.read_u16());
// Return the constant header size
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_u16(self.id));
try!(buffer.write_u8( ((self.recursion_desired as u8)) |
((self.truncated_message as u8) << 1) |
((self.authoritative_answer as u8) << 2) |
(self.opcode << 3) |
((self.response as u8) << 7) as u8) );
try!(buffer.write_u8( (self.rescode.clone() as u8) |
((self.checking_disabled as u8) << 4) |
((self.authed_data as u8) << 5) |
((self.z as u8) << 6) |
((self.recursion_available as u8) << 7) ));
try!(buffer.write_u16(self.questions));
try!(buffer.write_u16(self.answers));
try!(buffer.write_u16(self.authoritative_entries));
try!(buffer.write_u16(self.resource_entries));
Ok(())
}
}
#[derive(PartialEq,Eq,Debug,Clone,Hash,Copy)]
pub enum QueryType {
UNKNOWN(u16),
A, // 1
NS, // 2
CNAME, // 5
MX, // 15
AAAA, // 28
}
impl QueryType {
pub fn to_num(&self) -> u16 {
match *self {
QueryType::UNKNOWN(x) => x,
QueryType::A => 1,
QueryType::NS => 2,
QueryType::CNAME => 5,
QueryType::MX => 15,
QueryType::AAAA => 28,
}
}
pub fn from_num(num: u16) -> QueryType {
match num {
1 => QueryType::A,
2 => QueryType::NS,
5 => QueryType::CNAME,
15 => QueryType::MX,
28 => QueryType::AAAA,
_ => QueryType::UNKNOWN(num)
}
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub struct DnsQuestion {
pub name: String,
pub qtype: QueryType
}
impl DnsQuestion {
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
DnsQuestion {
name: name,
qtype: qtype
}
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.read_qname(&mut self.name));
self.qtype = QueryType::from_num(try!(buffer.read_u16())); // qtype
let _ = try!(buffer.read_u16()); // class
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_qname(&self.name));
let typenum = self.qtype.to_num();
try!(buffer.write_u16(typenum));
try!(buffer.write_u16(1));
Ok(())
}
}
#[derive(Debug,Clone,PartialEq,Eq,Hash,PartialOrd,Ord)]
#[allow(dead_code)]
pub enum DnsRecord {
UNKNOWN {
domain: String,
qtype: u16,
data_len: u16,
ttl: u32
}, // 0
A {
domain: String,
addr: Ipv4Addr,
ttl: u32
}, // 1
NS {
domain: String,
host: String,
ttl: u32
}, // 2
CNAME {
domain: String,
host: String,
ttl: u32
}, // 5
MX {
domain: String,
priority: u16,
host: String,
ttl: u32
}, // 15
AAAA {
domain: String,
addr: Ipv6Addr,
ttl: u32
}, // 28
}
impl DnsRecord {
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
let mut domain = String::new();
try!(buffer.read_qname(&mut domain));
let qtype_num = try!(buffer.read_u16());
let qtype = QueryType::from_num(qtype_num);
let _ = try!(buffer.read_u16());
let ttl = try!(buffer.read_u32());
let data_len = try!(buffer.read_u16());
match qtype {
QueryType::A => {
let raw_addr = try!(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 >> 0) & 0xFF) as u8);
Ok(DnsRecord::A {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::AAAA => {
let raw_addr1 = try!(buffer.read_u32());
let raw_addr2 = try!(buffer.read_u32());
let raw_addr3 = try!(buffer.read_u32());
let raw_addr4 = try!(buffer.read_u32());
let addr = Ipv6Addr::new(((raw_addr1 >> 16) & 0xFFFF) as u16,
((raw_addr1 >> 0) & 0xFFFF) as u16,
((raw_addr2 >> 16) & 0xFFFF) as u16,
((raw_addr2 >> 0) & 0xFFFF) as u16,
((raw_addr3 >> 16) & 0xFFFF) as u16,
((raw_addr3 >> 0) & 0xFFFF) as u16,
((raw_addr4 >> 16) & 0xFFFF) as u16,
((raw_addr4 >> 0) & 0xFFFF) as u16);
Ok(DnsRecord::AAAA {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::NS => {
let mut ns = String::new();
try!(buffer.read_qname(&mut ns));
Ok(DnsRecord::NS {
domain: domain,
host: ns,
ttl: ttl
})
},
QueryType::CNAME => {
let mut cname = String::new();
try!(buffer.read_qname(&mut cname));
Ok(DnsRecord::CNAME {
domain: domain,
host: cname,
ttl: ttl
})
},
QueryType::MX => {
let priority = try!(buffer.read_u16());
let mut mx = String::new();
try!(buffer.read_qname(&mut mx));
Ok(DnsRecord::MX {
domain: domain,
priority: priority,
host: mx,
ttl: ttl
})
},
QueryType::UNKNOWN(_) => {
try!(buffer.step(data_len as usize));
Ok(DnsRecord::UNKNOWN {
domain: domain,
qtype: qtype_num,
data_len: data_len,
ttl: ttl
})
}
}
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
match *self {
DnsRecord::A { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::A.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(4));
let octets = addr.octets();
try!(buffer.write_u8(octets[0]));
try!(buffer.write_u8(octets[1]));
try!(buffer.write_u8(octets[2]));
try!(buffer.write_u8(octets[3]));
},
DnsRecord::NS { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::NS.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::CNAME { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::CNAME.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::MX { ref domain, priority, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::MX.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_u16(priority));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::AAAA { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::AAAA.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(16));
for octet in &addr.segments() {
try!(buffer.write_u16(*octet));
}
},
DnsRecord::UNKNOWN { .. } => {
println!("Skipping record: {:?}", self);
}
}
Ok(buffer.pos() - start_pos)
}
}
#[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> {
let mut result = DnsPacket::new();
try!(result.header.read(buffer));
for _ in 0..result.header.questions {
let mut question = DnsQuestion::new("".to_string(),
QueryType::UNKNOWN(0));
try!(question.read(buffer));
result.questions.push(question);
}
for _ in 0..result.header.answers {
let rec = try!(DnsRecord::read(buffer));
result.answers.push(rec);
}
for _ in 0..result.header.authoritative_entries {
let rec = try!(DnsRecord::read(buffer));
result.authorities.push(rec);
}
for _ in 0..result.header.resource_entries {
let rec = try!(DnsRecord::read(buffer));
result.resources.push(rec);
}
Ok(result)
}
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()>
{
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;
try!(self.header.write(buffer));
for question in &self.questions {
try!(question.write(buffer));
}
for rec in &self.answers {
try!(rec.write(buffer));
}
for rec in &self.authorities {
try!(rec.write(buffer));
}
for rec in &self.resources {
try!(rec.write(buffer));
}
Ok(())
}
}
fn main() {
let qname = "yahoo.com";
let qtype = QueryType::MX;
let server = ("8.8.8.8", 53);
let socket = UdpSocket::bind(("0.0.0.0", 43210)).unwrap();
let mut packet = DnsPacket::new();
packet.header.id = 6666;
packet.header.questions = 1;
packet.header.recursion_desired = true;
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
socket.send_to(&req_buffer.buf[0..req_buffer.pos], server).unwrap();
let mut res_buffer = BytePacketBuffer::new();
socket.recv_from(&mut res_buffer.buf).unwrap();
let res_packet = DnsPacket::from_buffer(&mut res_buffer).unwrap();
println!("{:?}", res_packet.header);
for q in res_packet.questions {
println!("{:?}", q);
}
for rec in res_packet.answers {
println!("{:?}", rec);
}
for rec in res_packet.authorities {
println!("{:?}", rec);
}
for rec in res_packet.resources {
println!("{:?}", rec);
}
}

768
samples/sample4.rs Normal file
View File

@ -0,0 +1,768 @@
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::{Ipv4Addr,Ipv6Addr};
use std::net::UdpSocket;
pub struct BytePacketBuffer {
pub buf: [u8; 512],
pub pos: usize
}
impl BytePacketBuffer {
pub fn new() -> BytePacketBuffer {
BytePacketBuffer {
buf: [0; 512],
pos: 0
}
}
fn pos(&self) -> usize {
self.pos
}
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
let res = self.buf[self.pos];
self.pos += 1;
Ok(res)
}
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(self.buf[pos])
}
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(&self.buf[start..start+len as usize])
}
fn read_u16(&mut self) -> Result<u16>
{
let res = ((try!(self.read()) as u16) << 8) |
(try!(self.read()) as u16);
Ok(res)
}
fn read_u32(&mut self) -> Result<u32>
{
let res = ((try!(self.read()) as u32) << 24) |
((try!(self.read()) as u32) << 16) |
((try!(self.read()) as u32) << 8) |
((try!(self.read()) as u32) << 0);
Ok(res)
}
fn read_qname(&mut self, outstr: &mut String) -> Result<()>
{
let mut pos = self.pos();
let mut jumped = false;
let mut delim = "";
loop {
let len = try!(self.get(pos));
// A two byte sequence, where the two highest bits of the first byte is
// set, represents a offset relative to the start of the buffer. We
// handle this by jumping to the offset, setting a flag to indicate
// that we shouldn't update the shared buffer position once done.
if (len & 0xC0) > 0 {
// When a jump is performed, we only modify the shared buffer
// position once, and avoid making the change later on.
if !jumped {
try!(self.seek(pos+2));
}
let b2 = try!(self.get(pos+1)) as u16;
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
pos = offset as usize;
jumped = true;
continue;
}
pos += 1;
// Names are terminated by an empty label of length 0
if len == 0 {
break;
}
outstr.push_str(delim);
let str_buffer = try!(self.get_range(pos, len as usize));
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
pos += len as usize;
}
if !jumped {
try!(self.seek(pos));
}
Ok(())
}
fn write(&mut self, val: u8) -> Result<()> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
self.buf[self.pos] = val;
self.pos += 1;
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<()> {
try!(self.write(val));
Ok(())
}
fn write_u16(&mut self, val: u16) -> Result<()> {
try!(self.write((val >> 8) as u8));
try!(self.write((val & 0xFF) as u8));
Ok(())
}
fn write_u32(&mut self, val: u32) -> Result<()> {
try!(self.write(((val >> 24) & 0xFF) as u8));
try!(self.write(((val >> 16) & 0xFF) as u8));
try!(self.write(((val >> 8) & 0xFF) as u8));
try!(self.write(((val >> 0) & 0xFF) as u8));
Ok(())
}
fn write_qname(&mut self, qname: &str) -> Result<()> {
let split_str = qname.split('.').collect::<Vec<&str>>();
for label in split_str {
let len = label.len();
if len > 0x34 {
return Err(Error::new(ErrorKind::InvalidInput, "Single label exceeds 63 characters of length"));
}
try!(self.write_u8(len as u8));
for b in label.as_bytes() {
try!(self.write_u8(*b));
}
}
try!(self.write_u8(0));
Ok(())
}
fn set(&mut self, pos: usize, val: u8) -> Result<()> {
self.buf[pos] = val;
Ok(())
}
fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
try!(self.set(pos,(val >> 8) as u8));
try!(self.set(pos+1,(val & 0xFF) as u8));
Ok(())
}
}
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum ResultCode {
NOERROR = 0,
FORMERR = 1,
SERVFAIL = 2,
NXDOMAIN = 3,
NOTIMP = 4,
REFUSED = 5
}
impl ResultCode {
pub fn from_num(num: u8) -> ResultCode {
match num {
1 => ResultCode::FORMERR,
2 => ResultCode::SERVFAIL,
3 => ResultCode::NXDOMAIN,
4 => ResultCode::NOTIMP,
5 => ResultCode::REFUSED,
0 | _ => ResultCode::NOERROR
}
}
}
#[derive(Clone,Debug)]
pub struct DnsHeader {
pub id: u16, // 16 bits
pub recursion_desired: bool, // 1 bit
pub truncated_message: bool, // 1 bit
pub authoritative_answer: bool, // 1 bit
pub opcode: u8, // 4 bits
pub response: bool, // 1 bit
pub rescode: ResultCode, // 4 bits
pub checking_disabled: bool, // 1 bit
pub authed_data: bool, // 1 bit
pub z: bool, // 1 bit
pub recursion_available: bool, // 1 bit
pub questions: u16, // 16 bits
pub answers: u16, // 16 bits
pub authoritative_entries: u16, // 16 bits
pub resource_entries: u16 // 16 bits
}
impl DnsHeader {
pub fn new() -> DnsHeader {
DnsHeader { id: 0,
recursion_desired: false,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: false,
rescode: ResultCode::NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: false,
questions: 0,
answers: 0,
authoritative_entries: 0,
resource_entries: 0 }
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
self.id = try!(buffer.read_u16());
let flags = try!(buffer.read_u16());
let a = (flags >> 8) as u8;
let b = (flags & 0xFF) as u8;
self.recursion_desired = (a & (1 << 0)) > 0;
self.truncated_message = (a & (1 << 1)) > 0;
self.authoritative_answer = (a & (1 << 2)) > 0;
self.opcode = (a >> 3) & 0x0F;
self.response = (a & (1 << 7)) > 0;
self.rescode = ResultCode::from_num(b & 0x0F);
self.checking_disabled = (b & (1 << 4)) > 0;
self.authed_data = (b & (1 << 5)) > 0;
self.z = (b & (1 << 6)) > 0;
self.recursion_available = (b & (1 << 7)) > 0;
self.questions = try!(buffer.read_u16());
self.answers = try!(buffer.read_u16());
self.authoritative_entries = try!(buffer.read_u16());
self.resource_entries = try!(buffer.read_u16());
// Return the constant header size
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_u16(self.id));
try!(buffer.write_u8( ((self.recursion_desired as u8)) |
((self.truncated_message as u8) << 1) |
((self.authoritative_answer as u8) << 2) |
(self.opcode << 3) |
((self.response as u8) << 7) as u8) );
try!(buffer.write_u8( (self.rescode.clone() as u8) |
((self.checking_disabled as u8) << 4) |
((self.authed_data as u8) << 5) |
((self.z as u8) << 6) |
((self.recursion_available as u8) << 7) ));
try!(buffer.write_u16(self.questions));
try!(buffer.write_u16(self.answers));
try!(buffer.write_u16(self.authoritative_entries));
try!(buffer.write_u16(self.resource_entries));
Ok(())
}
}
#[derive(PartialEq,Eq,Debug,Clone,Hash,Copy)]
pub enum QueryType {
UNKNOWN(u16),
A, // 1
NS, // 2
CNAME, // 5
MX, // 15
AAAA, // 28
}
impl QueryType {
pub fn to_num(&self) -> u16 {
match *self {
QueryType::UNKNOWN(x) => x,
QueryType::A => 1,
QueryType::NS => 2,
QueryType::CNAME => 5,
QueryType::MX => 15,
QueryType::AAAA => 28,
}
}
pub fn from_num(num: u16) -> QueryType {
match num {
1 => QueryType::A,
2 => QueryType::NS,
5 => QueryType::CNAME,
15 => QueryType::MX,
28 => QueryType::AAAA,
_ => QueryType::UNKNOWN(num)
}
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub struct DnsQuestion {
pub name: String,
pub qtype: QueryType
}
impl DnsQuestion {
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
DnsQuestion {
name: name,
qtype: qtype
}
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.read_qname(&mut self.name));
self.qtype = QueryType::from_num(try!(buffer.read_u16())); // qtype
let _ = try!(buffer.read_u16()); // class
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_qname(&self.name));
let typenum = self.qtype.to_num();
try!(buffer.write_u16(typenum));
try!(buffer.write_u16(1));
Ok(())
}
}
#[derive(Debug,Clone,PartialEq,Eq,Hash,PartialOrd,Ord)]
#[allow(dead_code)]
pub enum DnsRecord {
UNKNOWN {
domain: String,
qtype: u16,
data_len: u16,
ttl: u32
}, // 0
A {
domain: String,
addr: Ipv4Addr,
ttl: u32
}, // 1
NS {
domain: String,
host: String,
ttl: u32
}, // 2
CNAME {
domain: String,
host: String,
ttl: u32
}, // 5
MX {
domain: String,
priority: u16,
host: String,
ttl: u32
}, // 15
AAAA {
domain: String,
addr: Ipv6Addr,
ttl: u32
}, // 28
}
impl DnsRecord {
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
let mut domain = String::new();
try!(buffer.read_qname(&mut domain));
let qtype_num = try!(buffer.read_u16());
let qtype = QueryType::from_num(qtype_num);
let _ = try!(buffer.read_u16());
let ttl = try!(buffer.read_u32());
let data_len = try!(buffer.read_u16());
match qtype {
QueryType::A => {
let raw_addr = try!(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 >> 0) & 0xFF) as u8);
Ok(DnsRecord::A {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::AAAA => {
let raw_addr1 = try!(buffer.read_u32());
let raw_addr2 = try!(buffer.read_u32());
let raw_addr3 = try!(buffer.read_u32());
let raw_addr4 = try!(buffer.read_u32());
let addr = Ipv6Addr::new(((raw_addr1 >> 16) & 0xFFFF) as u16,
((raw_addr1 >> 0) & 0xFFFF) as u16,
((raw_addr2 >> 16) & 0xFFFF) as u16,
((raw_addr2 >> 0) & 0xFFFF) as u16,
((raw_addr3 >> 16) & 0xFFFF) as u16,
((raw_addr3 >> 0) & 0xFFFF) as u16,
((raw_addr4 >> 16) & 0xFFFF) as u16,
((raw_addr4 >> 0) & 0xFFFF) as u16);
Ok(DnsRecord::AAAA {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::NS => {
let mut ns = String::new();
try!(buffer.read_qname(&mut ns));
Ok(DnsRecord::NS {
domain: domain,
host: ns,
ttl: ttl
})
},
QueryType::CNAME => {
let mut cname = String::new();
try!(buffer.read_qname(&mut cname));
Ok(DnsRecord::CNAME {
domain: domain,
host: cname,
ttl: ttl
})
},
QueryType::MX => {
let priority = try!(buffer.read_u16());
let mut mx = String::new();
try!(buffer.read_qname(&mut mx));
Ok(DnsRecord::MX {
domain: domain,
priority: priority,
host: mx,
ttl: ttl
})
},
QueryType::UNKNOWN(_) => {
try!(buffer.step(data_len as usize));
Ok(DnsRecord::UNKNOWN {
domain: domain,
qtype: qtype_num,
data_len: data_len,
ttl: ttl
})
}
}
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
match *self {
DnsRecord::A { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::A.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(4));
let octets = addr.octets();
try!(buffer.write_u8(octets[0]));
try!(buffer.write_u8(octets[1]));
try!(buffer.write_u8(octets[2]));
try!(buffer.write_u8(octets[3]));
},
DnsRecord::NS { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::NS.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::CNAME { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::CNAME.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::MX { ref domain, priority, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::MX.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_u16(priority));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::AAAA { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::AAAA.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(16));
for octet in &addr.segments() {
try!(buffer.write_u16(*octet));
}
},
DnsRecord::UNKNOWN { .. } => {
println!("Skipping record: {:?}", self);
}
}
Ok(buffer.pos() - start_pos)
}
}
#[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> {
let mut result = DnsPacket::new();
try!(result.header.read(buffer));
for _ in 0..result.header.questions {
let mut question = DnsQuestion::new("".to_string(),
QueryType::UNKNOWN(0));
try!(question.read(buffer));
result.questions.push(question);
}
for _ in 0..result.header.answers {
let rec = try!(DnsRecord::read(buffer));
result.answers.push(rec);
}
for _ in 0..result.header.authoritative_entries {
let rec = try!(DnsRecord::read(buffer));
result.authorities.push(rec);
}
for _ in 0..result.header.resource_entries {
let rec = try!(DnsRecord::read(buffer));
result.resources.push(rec);
}
Ok(result)
}
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()>
{
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;
try!(self.header.write(buffer));
for question in &self.questions {
try!(question.write(buffer));
}
for rec in &self.answers {
try!(rec.write(buffer));
}
for rec in &self.authorities {
try!(rec.write(buffer));
}
for rec in &self.resources {
try!(rec.write(buffer));
}
Ok(())
}
}
fn lookup(qname: &str, qtype: QueryType, server: (&str, u16)) -> Result<DnsPacket> {
let socket = try!(UdpSocket::bind(("0.0.0.0", 43210)));
let mut packet = DnsPacket::new();
packet.header.id = 6666;
packet.header.questions = 1;
packet.header.recursion_desired = true;
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
try!(socket.send_to(&req_buffer.buf[0..req_buffer.pos], server));
let mut res_buffer = BytePacketBuffer::new();
socket.recv_from(&mut res_buffer.buf).unwrap();
DnsPacket::from_buffer(&mut res_buffer)
}
fn main() {
let server = ("8.8.8.8", 53);
let socket = UdpSocket::bind(("0.0.0.0", 2053)).unwrap();
loop {
let mut req_buffer = BytePacketBuffer::new();
let (_, src) = match socket.recv_from(&mut req_buffer.buf) {
Ok(x) => x,
Err(e) => {
println!("Failed to read from UDP socket: {:?}", e);
continue;
}
};
let request = match DnsPacket::from_buffer(&mut req_buffer) {
Ok(x) => x,
Err(e) => {
println!("Failed to parse UDP query packet: {:?}", e);
continue;
}
};
let mut packet = DnsPacket::new();
packet.header.id = request.header.id;
packet.header.recursion_desired = true;
packet.header.recursion_available = true;
packet.header.response = true;
if request.questions.is_empty() {
packet.header.rescode = ResultCode::FORMERR;
}
else {
let question = &request.questions[0];
println!("Received query: {:?}", question);
if let Ok(result) = lookup(&question.name, question.qtype, server) {
packet.questions.push(question.clone());
packet.header.rescode = result.header.rescode;
for rec in result.answers {
println!("Answer: {:?}", rec);
packet.answers.push(rec);
}
for rec in result.authorities {
println!("Authority: {:?}", rec);
packet.authorities.push(rec);
}
for rec in result.resources {
println!("Resource: {:?}", rec);
packet.resources.push(rec);
}
} else {
packet.header.rescode = ResultCode::SERVFAIL;
}
}
let mut res_buffer = BytePacketBuffer::new();
match packet.write(&mut res_buffer) {
Ok(_) => {},
Err(e) => {
println!("Failed to encode UDP response packet: {:?}", e);
continue;
}
};
let len = res_buffer.pos();
let data = match res_buffer.get_range(0, len) {
Ok(x) => x,
Err(e) => {
println!("Failed to retrieve response buffer: {:?}", e);
continue;
}
};
match socket.send_to(data, src) {
Ok(_) => {},
Err(e) => {
println!("Failed to send response buffer: {:?}", e);
continue;
}
};
}
}

885
samples/sample5.rs Normal file
View File

@ -0,0 +1,885 @@
use std::io::{Result, Read};
use std::io::{Error, ErrorKind};
use std::net::{Ipv4Addr,Ipv6Addr};
use std::net::UdpSocket;
pub struct BytePacketBuffer {
pub buf: [u8; 512],
pub pos: usize
}
impl BytePacketBuffer {
pub fn new() -> BytePacketBuffer {
BytePacketBuffer {
buf: [0; 512],
pos: 0
}
}
fn pos(&self) -> usize {
self.pos
}
fn step(&mut self, steps: usize) -> Result<()> {
self.pos += steps;
Ok(())
}
fn seek(&mut self, pos: usize) -> Result<()> {
self.pos = pos;
Ok(())
}
fn read(&mut self) -> Result<u8> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
let res = self.buf[self.pos];
self.pos += 1;
Ok(res)
}
fn get(&mut self, pos: usize) -> Result<u8> {
if pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(self.buf[pos])
}
fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
if start + len >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
Ok(&self.buf[start..start+len as usize])
}
fn read_u16(&mut self) -> Result<u16>
{
let res = ((try!(self.read()) as u16) << 8) |
(try!(self.read()) as u16);
Ok(res)
}
fn read_u32(&mut self) -> Result<u32>
{
let res = ((try!(self.read()) as u32) << 24) |
((try!(self.read()) as u32) << 16) |
((try!(self.read()) as u32) << 8) |
((try!(self.read()) as u32) << 0);
Ok(res)
}
fn read_qname(&mut self, outstr: &mut String) -> Result<()>
{
let mut pos = self.pos();
let mut jumped = false;
let mut delim = "";
loop {
let len = try!(self.get(pos));
// A two byte sequence, where the two highest bits of the first byte is
// set, represents a offset relative to the start of the buffer. We
// handle this by jumping to the offset, setting a flag to indicate
// that we shouldn't update the shared buffer position once done.
if (len & 0xC0) > 0 {
// When a jump is performed, we only modify the shared buffer
// position once, and avoid making the change later on.
if !jumped {
try!(self.seek(pos+2));
}
let b2 = try!(self.get(pos+1)) as u16;
let offset = (((len as u16) ^ 0xC0) << 8) | b2;
pos = offset as usize;
jumped = true;
continue;
}
pos += 1;
// Names are terminated by an empty label of length 0
if len == 0 {
break;
}
outstr.push_str(delim);
let str_buffer = try!(self.get_range(pos, len as usize));
outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
delim = ".";
pos += len as usize;
}
if !jumped {
try!(self.seek(pos));
}
Ok(())
}
fn write(&mut self, val: u8) -> Result<()> {
if self.pos >= 512 {
return Err(Error::new(ErrorKind::InvalidInput, "End of buffer"));
}
self.buf[self.pos] = val;
self.pos += 1;
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<()> {
try!(self.write(val));
Ok(())
}
fn write_u16(&mut self, val: u16) -> Result<()> {
try!(self.write((val >> 8) as u8));
try!(self.write((val & 0xFF) as u8));
Ok(())
}
fn write_u32(&mut self, val: u32) -> Result<()> {
try!(self.write(((val >> 24) & 0xFF) as u8));
try!(self.write(((val >> 16) & 0xFF) as u8));
try!(self.write(((val >> 8) & 0xFF) as u8));
try!(self.write(((val >> 0) & 0xFF) as u8));
Ok(())
}
fn write_qname(&mut self, qname: &str) -> Result<()> {
let split_str = qname.split('.').collect::<Vec<&str>>();
for label in split_str {
let len = label.len();
if len > 0x34 {
return Err(Error::new(ErrorKind::InvalidInput, "Single label exceeds 63 characters of length"));
}
try!(self.write_u8(len as u8));
for b in label.as_bytes() {
try!(self.write_u8(*b));
}
}
try!(self.write_u8(0));
Ok(())
}
fn set(&mut self, pos: usize, val: u8) -> Result<()> {
self.buf[pos] = val;
Ok(())
}
fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
try!(self.set(pos,(val >> 8) as u8));
try!(self.set(pos+1,(val & 0xFF) as u8));
Ok(())
}
}
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum ResultCode {
NOERROR = 0,
FORMERR = 1,
SERVFAIL = 2,
NXDOMAIN = 3,
NOTIMP = 4,
REFUSED = 5
}
impl ResultCode {
pub fn from_num(num: u8) -> ResultCode {
match num {
1 => ResultCode::FORMERR,
2 => ResultCode::SERVFAIL,
3 => ResultCode::NXDOMAIN,
4 => ResultCode::NOTIMP,
5 => ResultCode::REFUSED,
0 | _ => ResultCode::NOERROR
}
}
}
#[derive(Clone,Debug)]
pub struct DnsHeader {
pub id: u16, // 16 bits
pub recursion_desired: bool, // 1 bit
pub truncated_message: bool, // 1 bit
pub authoritative_answer: bool, // 1 bit
pub opcode: u8, // 4 bits
pub response: bool, // 1 bit
pub rescode: ResultCode, // 4 bits
pub checking_disabled: bool, // 1 bit
pub authed_data: bool, // 1 bit
pub z: bool, // 1 bit
pub recursion_available: bool, // 1 bit
pub questions: u16, // 16 bits
pub answers: u16, // 16 bits
pub authoritative_entries: u16, // 16 bits
pub resource_entries: u16 // 16 bits
}
impl DnsHeader {
pub fn new() -> DnsHeader {
DnsHeader { id: 0,
recursion_desired: false,
truncated_message: false,
authoritative_answer: false,
opcode: 0,
response: false,
rescode: ResultCode::NOERROR,
checking_disabled: false,
authed_data: false,
z: false,
recursion_available: false,
questions: 0,
answers: 0,
authoritative_entries: 0,
resource_entries: 0 }
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
self.id = try!(buffer.read_u16());
let flags = try!(buffer.read_u16());
let a = (flags >> 8) as u8;
let b = (flags & 0xFF) as u8;
self.recursion_desired = (a & (1 << 0)) > 0;
self.truncated_message = (a & (1 << 1)) > 0;
self.authoritative_answer = (a & (1 << 2)) > 0;
self.opcode = (a >> 3) & 0x0F;
self.response = (a & (1 << 7)) > 0;
self.rescode = ResultCode::from_num(b & 0x0F);
self.checking_disabled = (b & (1 << 4)) > 0;
self.authed_data = (b & (1 << 5)) > 0;
self.z = (b & (1 << 6)) > 0;
self.recursion_available = (b & (1 << 7)) > 0;
self.questions = try!(buffer.read_u16());
self.answers = try!(buffer.read_u16());
self.authoritative_entries = try!(buffer.read_u16());
self.resource_entries = try!(buffer.read_u16());
// Return the constant header size
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_u16(self.id));
try!(buffer.write_u8( ((self.recursion_desired as u8)) |
((self.truncated_message as u8) << 1) |
((self.authoritative_answer as u8) << 2) |
(self.opcode << 3) |
((self.response as u8) << 7) as u8) );
try!(buffer.write_u8( (self.rescode.clone() as u8) |
((self.checking_disabled as u8) << 4) |
((self.authed_data as u8) << 5) |
((self.z as u8) << 6) |
((self.recursion_available as u8) << 7) ));
try!(buffer.write_u16(self.questions));
try!(buffer.write_u16(self.answers));
try!(buffer.write_u16(self.authoritative_entries));
try!(buffer.write_u16(self.resource_entries));
Ok(())
}
}
#[derive(PartialEq,Eq,Debug,Clone,Hash,Copy)]
pub enum QueryType {
UNKNOWN(u16),
A, // 1
NS, // 2
CNAME, // 5
MX, // 15
AAAA, // 28
}
impl QueryType {
pub fn to_num(&self) -> u16 {
match *self {
QueryType::UNKNOWN(x) => x,
QueryType::A => 1,
QueryType::NS => 2,
QueryType::CNAME => 5,
QueryType::MX => 15,
QueryType::AAAA => 28,
}
}
pub fn from_num(num: u16) -> QueryType {
match num {
1 => QueryType::A,
2 => QueryType::NS,
5 => QueryType::CNAME,
15 => QueryType::MX,
28 => QueryType::AAAA,
_ => QueryType::UNKNOWN(num)
}
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub struct DnsQuestion {
pub name: String,
pub qtype: QueryType
}
impl DnsQuestion {
pub fn new(name: String, qtype: QueryType) -> DnsQuestion {
DnsQuestion {
name: name,
qtype: qtype
}
}
pub fn read(&mut self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.read_qname(&mut self.name));
self.qtype = QueryType::from_num(try!(buffer.read_u16())); // qtype
let _ = try!(buffer.read_u16()); // class
Ok(())
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
try!(buffer.write_qname(&self.name));
let typenum = self.qtype.to_num();
try!(buffer.write_u16(typenum));
try!(buffer.write_u16(1));
Ok(())
}
}
#[derive(Debug,Clone,PartialEq,Eq,Hash,PartialOrd,Ord)]
#[allow(dead_code)]
pub enum DnsRecord {
UNKNOWN {
domain: String,
qtype: u16,
data_len: u16,
ttl: u32
}, // 0
A {
domain: String,
addr: Ipv4Addr,
ttl: u32
}, // 1
NS {
domain: String,
host: String,
ttl: u32
}, // 2
CNAME {
domain: String,
host: String,
ttl: u32
}, // 5
MX {
domain: String,
priority: u16,
host: String,
ttl: u32
}, // 15
AAAA {
domain: String,
addr: Ipv6Addr,
ttl: u32
}, // 28
}
impl DnsRecord {
pub fn read(buffer: &mut BytePacketBuffer) -> Result<DnsRecord> {
let mut domain = String::new();
try!(buffer.read_qname(&mut domain));
let qtype_num = try!(buffer.read_u16());
let qtype = QueryType::from_num(qtype_num);
let _ = try!(buffer.read_u16());
let ttl = try!(buffer.read_u32());
let data_len = try!(buffer.read_u16());
match qtype {
QueryType::A => {
let raw_addr = try!(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 >> 0) & 0xFF) as u8);
Ok(DnsRecord::A {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::AAAA => {
let raw_addr1 = try!(buffer.read_u32());
let raw_addr2 = try!(buffer.read_u32());
let raw_addr3 = try!(buffer.read_u32());
let raw_addr4 = try!(buffer.read_u32());
let addr = Ipv6Addr::new(((raw_addr1 >> 16) & 0xFFFF) as u16,
((raw_addr1 >> 0) & 0xFFFF) as u16,
((raw_addr2 >> 16) & 0xFFFF) as u16,
((raw_addr2 >> 0) & 0xFFFF) as u16,
((raw_addr3 >> 16) & 0xFFFF) as u16,
((raw_addr3 >> 0) & 0xFFFF) as u16,
((raw_addr4 >> 16) & 0xFFFF) as u16,
((raw_addr4 >> 0) & 0xFFFF) as u16);
Ok(DnsRecord::AAAA {
domain: domain,
addr: addr,
ttl: ttl
})
},
QueryType::NS => {
let mut ns = String::new();
try!(buffer.read_qname(&mut ns));
Ok(DnsRecord::NS {
domain: domain,
host: ns,
ttl: ttl
})
},
QueryType::CNAME => {
let mut cname = String::new();
try!(buffer.read_qname(&mut cname));
Ok(DnsRecord::CNAME {
domain: domain,
host: cname,
ttl: ttl
})
},
QueryType::MX => {
let priority = try!(buffer.read_u16());
let mut mx = String::new();
try!(buffer.read_qname(&mut mx));
Ok(DnsRecord::MX {
domain: domain,
priority: priority,
host: mx,
ttl: ttl
})
},
QueryType::UNKNOWN(_) => {
try!(buffer.step(data_len as usize));
Ok(DnsRecord::UNKNOWN {
domain: domain,
qtype: qtype_num,
data_len: data_len,
ttl: ttl
})
}
}
}
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<usize> {
let start_pos = buffer.pos();
match *self {
DnsRecord::A { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::A.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(4));
let octets = addr.octets();
try!(buffer.write_u8(octets[0]));
try!(buffer.write_u8(octets[1]));
try!(buffer.write_u8(octets[2]));
try!(buffer.write_u8(octets[3]));
},
DnsRecord::NS { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::NS.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::CNAME { ref domain, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::CNAME.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::MX { ref domain, priority, ref host, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::MX.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
let pos = buffer.pos();
try!(buffer.write_u16(0));
try!(buffer.write_u16(priority));
try!(buffer.write_qname(host));
let size = buffer.pos() - (pos + 2);
try!(buffer.set_u16(pos, size as u16));
},
DnsRecord::AAAA { ref domain, ref addr, ttl } => {
try!(buffer.write_qname(domain));
try!(buffer.write_u16(QueryType::AAAA.to_num()));
try!(buffer.write_u16(1));
try!(buffer.write_u32(ttl));
try!(buffer.write_u16(16));
for octet in &addr.segments() {
try!(buffer.write_u16(*octet));
}
},
DnsRecord::UNKNOWN { .. } => {
println!("Skipping record: {:?}", self);
}
}
Ok(buffer.pos() - start_pos)
}
}
#[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> {
let mut result = DnsPacket::new();
try!(result.header.read(buffer));
for _ in 0..result.header.questions {
let mut question = DnsQuestion::new("".to_string(),
QueryType::UNKNOWN(0));
try!(question.read(buffer));
result.questions.push(question);
}
for _ in 0..result.header.answers {
let rec = try!(DnsRecord::read(buffer));
result.answers.push(rec);
}
for _ in 0..result.header.authoritative_entries {
let rec = try!(DnsRecord::read(buffer));
result.authorities.push(rec);
}
for _ in 0..result.header.resource_entries {
let rec = try!(DnsRecord::read(buffer));
result.resources.push(rec);
}
Ok(result)
}
pub fn write(&mut self, buffer: &mut BytePacketBuffer) -> Result<()>
{
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;
try!(self.header.write(buffer));
for question in &self.questions {
try!(question.write(buffer));
}
for rec in &self.answers {
try!(rec.write(buffer));
}
for rec in &self.authorities {
try!(rec.write(buffer));
}
for rec in &self.resources {
try!(rec.write(buffer));
}
Ok(())
}
pub fn get_random_a(&self) -> Option<String> {
if !self.answers.is_empty() {
let a_record = &self.answers[0];
if let DnsRecord::A{ ref addr, .. } = *a_record {
return Some(addr.to_string());
}
}
None
}
pub fn get_resolved_ns(&self, qname: &str) -> Option<String> {
let mut new_authorities = Vec::new();
for auth in &self.authorities {
if let DnsRecord::NS { ref domain, ref host, .. } = *auth {
if !qname.ends_with(domain) {
continue;
}
for rsrc in &self.resources {
if let DnsRecord::A{ ref domain, ref addr, ttl } = *rsrc {
if domain != host {
continue;
}
let rec = DnsRecord::A {
domain: host.clone(),
addr: *addr,
ttl: ttl
};
new_authorities.push(rec);
}
}
}
}
if !new_authorities.is_empty() {
if let DnsRecord::A { addr, .. } = new_authorities[0] {
return Some(addr.to_string());
}
}
None
}
pub fn get_unresolved_ns(&self, qname: &str) -> Option<String> {
let mut new_authorities = Vec::new();
for auth in &self.authorities {
if let DnsRecord::NS { ref domain, ref host, .. } = *auth {
if !qname.ends_with(domain) {
continue;
}
new_authorities.push(host);
}
}
if !new_authorities.is_empty() {
return Some(new_authorities[0].clone());
}
None
}
}
fn lookup(qname: &str, qtype: QueryType, server: (&str, u16)) -> Result<DnsPacket> {
let socket = try!(UdpSocket::bind(("0.0.0.0", 43210)));
let mut packet = DnsPacket::new();
packet.header.id = 6666;
packet.header.questions = 1;
packet.header.recursion_desired = true;
packet.questions.push(DnsQuestion::new(qname.to_string(), qtype));
let mut req_buffer = BytePacketBuffer::new();
packet.write(&mut req_buffer).unwrap();
try!(socket.send_to(&req_buffer.buf[0..req_buffer.pos], server));
let mut res_buffer = BytePacketBuffer::new();
socket.recv_from(&mut res_buffer.buf).unwrap();
DnsPacket::from_buffer(&mut res_buffer)
}
fn recursive_lookup(qname: &str, qtype: QueryType) -> Result<DnsPacket> {
let mut ns = "198.41.0.4".to_string();
// Start querying name servers
loop {
println!("attempting lookup of {:?} {} with ns {}", qtype, qname, ns);
let ns_copy = ns.clone();
let server = (ns_copy.as_str(), 53);
let response = try!(lookup(qname, qtype.clone(), server));
// If we've got an actual answer, we're done!
if !response.answers.is_empty() &&
response.header.rescode == ResultCode::NOERROR {
return Ok(response.clone());
}
if response.header.rescode == ResultCode::NXDOMAIN {
return Ok(response.clone());
}
// Otherwise, try to find a new nameserver based on NS and a
// corresponding A record in the additional section
if let Some(new_ns) = response.get_resolved_ns(qname) {
// If there is such a record, we can retry the loop with that NS
ns = new_ns.clone();
continue;
}
// If not, we'll have to resolve the ip of a NS record
let new_ns_name = match response.get_unresolved_ns(qname) {
Some(x) => x,
None => return Ok(response.clone())
};
// Recursively resolve the NS
let recursive_response = try!(recursive_lookup(&new_ns_name, QueryType::A));
// Pick a random IP and restart
if let Some(new_ns) = recursive_response.get_random_a() {
ns = new_ns.clone();
} else {
return Ok(response.clone())
}
}
}
fn main() {
let socket = UdpSocket::bind(("0.0.0.0", 2053)).unwrap();
loop {
let mut req_buffer = BytePacketBuffer::new();
let (_, src) = match socket.recv_from(&mut req_buffer.buf) {
Ok(x) => x,
Err(e) => {
println!("Failed to read from UDP socket: {:?}", e);
continue;
}
};
let request = match DnsPacket::from_buffer(&mut req_buffer) {
Ok(x) => x,
Err(e) => {
println!("Failed to parse UDP query packet: {:?}", e);
continue;
}
};
let mut packet = DnsPacket::new();
packet.header.id = request.header.id;
packet.header.recursion_desired = true;
packet.header.recursion_available = true;
packet.header.response = true;
if request.questions.is_empty() {
packet.header.rescode = ResultCode::FORMERR;
}
else {
let question = &request.questions[0];
println!("Received query: {:?}", question);
if let Ok(result) = recursive_lookup(&question.name, question.qtype) {
packet.questions.push(question.clone());
packet.header.rescode = result.header.rescode;
for rec in result.answers {
println!("Answer: {:?}", rec);
packet.answers.push(rec);
}
for rec in result.authorities {
println!("Authority: {:?}", rec);
packet.authorities.push(rec);
}
for rec in result.resources {
println!("Resource: {:?}", rec);
packet.resources.push(rec);
}
} else {
packet.header.rescode = ResultCode::SERVFAIL;
}
}
let mut res_buffer = BytePacketBuffer::new();
match packet.write(&mut res_buffer) {
Ok(_) => {},
Err(e) => {
println!("Failed to encode UDP response packet: {:?}", e);
continue;
}
};
let len = res_buffer.pos();
let data = match res_buffer.get_range(0, len) {
Ok(x) => x,
Err(e) => {
println!("Failed to retrieve response buffer: {:?}", e);
continue;
}
};
match socket.send_to(data, src) {
Ok(_) => {},
Err(e) => {
println!("Failed to send response buffer: {:?}", e);
continue;
}
};
}
}

View File

View File

@ -1,3 +0,0 @@
extern crate tango;
fn main() { tango::process_root().unwrap() }