Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.vagga
/target
/Cargo.lock
/.idea
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Next

- `Name` no longer assumes that labels are all ASCII, or text at all, since the DNS spec doesn't require it either.
- Notably, this allows parsing UTF-8 labels as used in mDNS.
- Unfortunately, this means that several types can no longer implement `ToString`, as there isn't an infallible
canonical text representation.
- `StrName` is provided for the common case of UTF-8 labels
- Update MSRV to 1.80.0 to allow `slice::split_at_checked`. This is older than rustc in Debian Stable (trixie, 1.85.0),
though newer than Ubuntu LTS (Noble Numbat, 1.75.0).
- Small syntactic tweaks to address all compiler and clippy warnings

# 0.8.0

Current version when changelog started
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[workspace]
members = ["fuzz"]

[package]
name = "dns-parser"
description = """
Expand All @@ -12,6 +15,7 @@ homepage = "https://github.com/tailhook/dns-parser"
documentation = "https://docs.rs/dns-parser"
version = "0.8.0"
authors = ["Paul Colomiets <paul@colomiets.name>"]
edition = "2015"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default value for when the edition is left unspecified


[features]
with-serde = ["serde", "serde_derive"]
Expand All @@ -23,5 +27,7 @@ byteorder = "1"
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }

hex = "0.4.3"

[dev-dependencies]
matches = "0.1.2"
itertools = "0.14.0"
29 changes: 14 additions & 15 deletions examples/sync_tcp_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ use std::io::{Read, Write};
use std::net::TcpStream;
use std::process;


use dns_parser::{Builder, Packet, RData, ResponseCode};
use dns_parser::rdata::a::Record;
use dns_parser::{QueryType, QueryClass};

use dns_parser::{Builder, Packet, RData, ResponseCode};
use dns_parser::{QueryClass, QueryType};

fn main() {
let mut code = 0;
for name in env::args().skip(1) {
match resolve(&name) {
Ok(()) => {},
Ok(()) => {}
Err(e) => {
eprintln!("Error resolving {:?}: {}", name, e);
code = 1;
Expand All @@ -26,13 +24,15 @@ fn main() {
process::exit(code);
}

fn resolve(name: &str) -> Result<(), Box<Error>> {
fn resolve(name: &str) -> Result<(), Box<dyn Error>> {
let mut conn = TcpStream::connect("127.0.0.1:53")?;
let mut builder = Builder::new_query(1, true);
builder.add_question(name, false, QueryType::A, QueryClass::IN);
let packet = builder.build().map_err(|_| "truncated packet")?;
let psize = [((packet.len() >> 8) & 0xFF) as u8,
(packet.len() & 0xFF) as u8];
let psize = [
((packet.len() >> 8) & 0xFF) as u8,
(packet.len() & 0xFF) as u8,
];
conn.write_all(&psize[..])?;
conn.write_all(&packet)?;
let mut buf = vec![0u8; 4096];
Expand All @@ -44,7 +44,9 @@ fn resolve(name: &str) -> Result<(), Box<Error>> {
match conn.read(&mut buf[off..]) {
Ok(num) => {
off += num;
if off < 2 { continue; }
if off < 2 {
continue;
}
let bytes = ((buf[0] as usize) << 8) | buf[1] as usize;
if off < bytes + 2 {
continue;
Expand All @@ -62,15 +64,12 @@ fn resolve(name: &str) -> Result<(), Box<Error>> {
if pkt.header.response_code != ResponseCode::NoError {
return Err(pkt.header.response_code.into());
}
if pkt.answers.len() == 0 {
if pkt.answers.is_empty() {
return Err("No records received".into());
}
for ans in pkt.answers {
match ans.data {
RData::A(Record(ip)) => {
println!("{}", ip);
}
_ => {} // ignore
if let RData::A(Record(ip)) = ans.data {
println!("{}", ip);
}
}
Ok(())
Expand Down
19 changes: 7 additions & 12 deletions examples/sync_udp_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ use std::error::Error;
use std::net::UdpSocket;
use std::process;


use dns_parser::{Builder, Packet, RData, ResponseCode};
use dns_parser::rdata::a::Record;
use dns_parser::{QueryType, QueryClass};

use dns_parser::{Builder, Packet, RData, ResponseCode};
use dns_parser::{QueryClass, QueryType};

fn main() {
let mut code = 0;
for name in env::args().skip(1) {
match resolve(&name) {
Ok(()) => {},
Ok(()) => {}
Err(e) => {
eprintln!("Error resolving {:?}: {}", name, e);
code = 1;
Expand All @@ -25,7 +23,7 @@ fn main() {
process::exit(code);
}

fn resolve(name: &str) -> Result<(), Box<Error>> {
fn resolve(name: &str) -> Result<(), Box<dyn Error>> {
let sock = UdpSocket::bind("127.0.0.1:0")?;
sock.connect("127.0.0.1:53")?;
let mut builder = Builder::new_query(1, true);
Expand All @@ -38,15 +36,12 @@ fn resolve(name: &str) -> Result<(), Box<Error>> {
if pkt.header.response_code != ResponseCode::NoError {
return Err(pkt.header.response_code.into());
}
if pkt.answers.len() == 0 {
if pkt.answers.is_empty() {
return Err("No records received".into());
}
for ans in pkt.answers {
match ans.data {
RData::A(Record(ip)) => {
println!("{}", ip);
}
_ => {} // ignore
if let RData::A(Record(ip)) = ans.data {
println!("{}", ip);
}
}
Ok(())
Expand Down
4 changes: 4 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
21 changes: 21 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "dns-parser-fuzz"
version = "0.0.0"
publish = false
edition = "2015"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"

[dependencies.dns-parser]
path = ".."

[[bin]]
name = "parse_packet"
path = "fuzz_targets/parse_packet.rs"
test = false
doc = false
bench = false
10 changes: 10 additions & 0 deletions fuzz/fuzz_targets/parse_packet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![no_main]

extern crate libfuzzer_sys;

use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
// not panicking is the goal
let _ = dns_parser::Packet::parse(data);
});
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "1.80.0"
31 changes: 18 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};

use {Opcode, ResponseCode, Header, QueryType, QueryClass};
use {Header, Opcode, QueryClass, QueryType, ResponseCode};

/// Allows to build a DNS packet
///
Expand All @@ -19,7 +19,7 @@ impl Builder {
pub fn new_query(id: u16, recursion: bool) -> Builder {
let mut buf = Vec::with_capacity(512);
let head = Header {
id: id,
id,
query: true,
opcode: Opcode::StandardQuery,
authoritative: false,
Expand All @@ -36,7 +36,7 @@ impl Builder {
};
buf.extend([0u8; 12].iter());
head.write(&mut buf[..12]);
Builder { buf: buf }
Builder { buf }
}
/// Adds a question to the packet
///
Expand All @@ -45,22 +45,27 @@ impl Builder {
/// * Answers, nameservers or additional section has already been written
/// * There are already 65535 questions in the buffer.
/// * When name is invalid
pub fn add_question(&mut self, qname: &str, prefer_unicast: bool,
qtype: QueryType, qclass: QueryClass)
-> &mut Builder
{
pub fn add_question(
&mut self,
qname: &str,
prefer_unicast: bool,
qtype: QueryType,
qclass: QueryClass,
) -> &mut Builder {
if &self.buf[6..12] != b"\x00\x00\x00\x00\x00\x00" {
panic!("Too late to add a question");
}
self.write_name(qname);
self.buf.write_u16::<BigEndian>(qtype as u16).unwrap();
let prefer_unicast: u16 = if prefer_unicast { 0x8000 } else { 0x0000 };
self.buf.write_u16::<BigEndian>(qclass as u16 | prefer_unicast).unwrap();
self.buf
.write_u16::<BigEndian>(qclass as u16 | prefer_unicast)
.unwrap();
let oldq = BigEndian::read_u16(&self.buf[4..6]);
if oldq == 65535 {
panic!("Too many questions");
}
BigEndian::write_u16(&mut self.buf[4..6], oldq+1);
BigEndian::write_u16(&mut self.buf[4..6], oldq + 1);
self
}
fn write_name(&mut self, name: &str) {
Expand All @@ -86,7 +91,7 @@ impl Builder {
/// appropriate.
// TODO(tailhook) does the truncation make sense for TCP, and how
// to treat it for EDNS0?
pub fn build(mut self) -> Result<Vec<u8>,Vec<u8>> {
pub fn build(mut self) -> Result<Vec<u8>, Vec<u8>> {
// TODO(tailhook) optimize labels
if self.buf.len() > 512 {
Header::set_truncated(&mut self.buf[..12]);
Expand All @@ -99,9 +104,9 @@ impl Builder {

#[cfg(test)]
mod test {
use QueryType as QT;
use QueryClass as QC;
use super::Builder;
use QueryClass as QC;
use QueryType as QT;

#[test]
fn build_query() {
Expand Down
Loading