From 269a30e904c9598c9a27b0f0e736b33b77decaf0 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Mon, 19 Feb 2024 20:08:45 -0600 Subject: [PATCH 01/10] preparing for grateness --- Cargo.lock | 96 +++++++++++++++++++++++++++++++++------- dns-transport/Cargo.toml | 2 +- dns/Cargo.toml | 5 ++- 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e116e3e..d7b7060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dns" -version = "0.2.0-pre" +version = "0.2.4-beta" dependencies = [ "base64 0.21.7", "byteorder", @@ -136,7 +136,7 @@ dependencies = [ [[package]] name = "dns-transport" -version = "0.2.0-pre" +version = "0.2.4-beta" dependencies = [ "cfg-if", "dns", @@ -172,7 +172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -519,7 +519,7 @@ dependencies = [ "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -532,7 +532,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -566,7 +566,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -686,7 +686,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -949,13 +949,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -964,51 +988,93 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.0" diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml index 56fafea..cafb9e6 100644 --- a/dns-transport/Cargo.toml +++ b/dns-transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dns-transport" -version = "0.2.0-pre" +version = "0.2.4-beta" authors = ["Benjamin Sago "] edition = "2018" diff --git a/dns/Cargo.toml b/dns/Cargo.toml index c4242b7..aff2dee 100644 --- a/dns/Cargo.toml +++ b/dns/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "dns" -version = "0.2.0-pre" -authors = ["Benjamin Sago "] +version = "0.2.4-beta" +author = ["Darrion Whitfield "] +# Original author authors = ["Benjamin Sago "] edition = "2018" [lib] From 8d95804069bf563857cbfe1ae57805d7d48efe15 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Tue, 20 Feb 2024 00:08:05 -0600 Subject: [PATCH 02/10] moved dns to a PUBLIC crate --- Cargo.lock | 110 +----- Cargo.toml | 5 +- dns-transport/Cargo.toml | 2 +- dns-transport/src/auto.rs | 2 +- dns-transport/src/error.rs | 6 +- dns-transport/src/https.rs | 2 +- dns-transport/src/lib.rs | 2 +- dns-transport/src/tcp.rs | 2 +- dns-transport/src/tls.rs | 2 +- dns-transport/src/udp.rs | 2 +- dns/Cargo.toml | 35 -- dns/fuzz/.gitignore | 4 - dns/fuzz/Cargo.lock | 62 ---- dns/fuzz/Cargo.toml | 22 -- dns/fuzz/fuzz_targets/fuzz_parsing.rs | 8 - dns/src/lib.rs | 44 --- dns/src/record/a.rs | 95 ----- dns/src/record/aaaa.rs | 97 ----- dns/src/record/caa.rs | 134 ------- dns/src/record/cname.rs | 86 ----- dns/src/record/eui48.rs | 111 ------ dns/src/record/eui64.rs | 111 ------ dns/src/record/hinfo.rs | 112 ------ dns/src/record/loc.rs | 493 -------------------------- dns/src/record/mod.rs | 238 ------------- dns/src/record/mx.rs | 97 ----- dns/src/record/naptr.rs | 160 --------- dns/src/record/ns.rs | 87 ----- dns/src/record/openpgpkey.rs | 91 ----- dns/src/record/opt.rs | 176 --------- dns/src/record/others.rs | 102 ------ dns/src/record/ptr.rs | 91 ----- dns/src/record/soa.rs | 153 -------- dns/src/record/srv.rs | 118 ------ dns/src/record/sshfp.rs | 140 -------- dns/src/record/tlsa.rs | 142 -------- dns/src/record/txt.rs | 231 ------------ dns/src/record/uri.rs | 123 ------- dns/src/strings.rs | 325 ----------------- dns/src/types.rs | 214 ----------- dns/src/wire.rs | 445 ----------------------- dns/tests/wire_building_tests.rs | 41 --- dns/tests/wire_parsing_tests.rs | 271 -------------- src/hints.rs | 6 +- src/main.rs | 6 +- src/options.rs | 6 +- src/output.rs | 4 +- src/requests.rs | 20 +- src/resolve.rs | 2 +- src/table.rs | 4 +- 50 files changed, 50 insertions(+), 4792 deletions(-) delete mode 100644 dns/Cargo.toml delete mode 100644 dns/fuzz/.gitignore delete mode 100644 dns/fuzz/Cargo.lock delete mode 100644 dns/fuzz/Cargo.toml delete mode 100644 dns/fuzz/fuzz_targets/fuzz_parsing.rs delete mode 100644 dns/src/lib.rs delete mode 100644 dns/src/record/a.rs delete mode 100644 dns/src/record/aaaa.rs delete mode 100644 dns/src/record/caa.rs delete mode 100644 dns/src/record/cname.rs delete mode 100644 dns/src/record/eui48.rs delete mode 100644 dns/src/record/eui64.rs delete mode 100644 dns/src/record/hinfo.rs delete mode 100644 dns/src/record/loc.rs delete mode 100644 dns/src/record/mod.rs delete mode 100644 dns/src/record/mx.rs delete mode 100644 dns/src/record/naptr.rs delete mode 100644 dns/src/record/ns.rs delete mode 100644 dns/src/record/openpgpkey.rs delete mode 100644 dns/src/record/opt.rs delete mode 100644 dns/src/record/others.rs delete mode 100644 dns/src/record/ptr.rs delete mode 100644 dns/src/record/soa.rs delete mode 100644 dns/src/record/srv.rs delete mode 100644 dns/src/record/sshfp.rs delete mode 100644 dns/src/record/tlsa.rs delete mode 100644 dns/src/record/txt.rs delete mode 100644 dns/src/record/uri.rs delete mode 100644 dns/src/strings.rs delete mode 100644 dns/src/types.rs delete mode 100644 dns/src/wire.rs delete mode 100644 dns/tests/wire_building_tests.rs delete mode 100644 dns/tests/wire_parsing_tests.rs diff --git a/Cargo.lock b/Cargo.lock index d7b7060..c3a4dfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "anyhow" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" - [[package]] name = "atty" version = "0.2.14" @@ -122,24 +116,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "dns" -version = "0.2.4-beta" -dependencies = [ - "base64 0.21.7", - "byteorder", - "log", - "mutagen", - "pretty_assertions", - "unic-idna", -] - [[package]] name = "dns-transport" version = "0.2.4-beta" dependencies = [ "cfg-if", - "dns", + "doge_dns", "httparse", "log", "native-tls", @@ -155,8 +137,8 @@ dependencies = [ "ansi_term", "atty", "datetime", - "dns", "dns-transport", + "doge_dns", "getopts", "ipconfig", "json", @@ -165,6 +147,18 @@ dependencies = [ "rand", ] +[[package]] +name = "doge_dns" +version = "0.2.4-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c198586e6649caf93c0932a982a7036dd4b34f1c47da1f9b669f872f98bb9405" +dependencies = [ + "base64 0.21.7", + "byteorder", + "log", + "unic-idna", +] + [[package]] name = "errno" version = "0.3.8" @@ -243,12 +237,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - [[package]] name = "js-sys" version = "0.3.68" @@ -294,39 +282,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" -[[package]] -name = "mutagen" -version = "0.2.0" -source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53" -dependencies = [ - "mutagen-core", - "mutagen-transform", -] - -[[package]] -name = "mutagen-core" -version = "0.2.0" -source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53" -dependencies = [ - "anyhow", - "json", - "lazy_static", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 1.0.109", -] - -[[package]] -name = "mutagen-transform" -version = "0.2.0" -source = "git+https://github.com/llogiq/mutagen#a6377c4c3f360afeb7a287c1c17e4b69456d5f53" -dependencies = [ - "mutagen-core", - "proc-macro2", -] - [[package]] name = "native-tls" version = "0.2.11" @@ -554,12 +509,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - [[package]] name = "schannel" version = "0.1.23" @@ -602,37 +551,6 @@ dependencies = [ "libc", ] -[[package]] -name = "serde" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", -] - -[[package]] -name = "serde_json" -version = "1.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "socket2" version = "0.5.5" diff --git a/Cargo.toml b/Cargo.toml index d4a1fc6..1569118 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ doctest = false [workspace] members = [ - "dns", "dns-transport", ] @@ -42,7 +41,7 @@ panic = "abort" [dependencies] # dns stuff -dns = { path = "./dns" } +doge_dns = "0.2.4-beta" dns-transport = { path = "./dns-transport" } # command-line @@ -71,7 +70,7 @@ pretty_assertions = "0.7" [features] default = ["with_idna", "with_tls", "with_https", "with_nativetls"] -with_idna = ["dns/with_idna"] +with_idna = ["doge_dns/with_idna"] with_tls = ["dns-transport/with_tls"] with_https = ["dns-transport/with_https"] diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml index cafb9e6..5a134b1 100644 --- a/dns-transport/Cargo.toml +++ b/dns-transport/Cargo.toml @@ -12,7 +12,7 @@ test = false [dependencies] # dns wire protocol -dns = { path = "../dns" } +doge_dns = "0.2.4-beta" # logging log = "0.4" diff --git a/dns-transport/src/auto.rs b/dns-transport/src/auto.rs index b1cc669..6487ab9 100644 --- a/dns-transport/src/auto.rs +++ b/dns-transport/src/auto.rs @@ -1,6 +1,6 @@ use log::*; -use dns::{Request, Response}; +use doge_dns::{Request, Response}; use crate::GenericTransport; use super::{Transport, Error, UdpTransport, TcpTransport}; diff --git a/dns-transport/src/error.rs b/dns-transport/src/error.rs index b95afba..2fda52b 100644 --- a/dns-transport/src/error.rs +++ b/dns-transport/src/error.rs @@ -4,7 +4,7 @@ pub enum Error { /// The data in the response did not parse correctly from the DNS wire /// protocol format. - WireError(dns::WireError), + WireError(doge_dns::WireError), /// There was a problem with the network making a TCP or UDP request. NetworkError(std::io::Error), @@ -46,8 +46,8 @@ impl From for Error { } } -impl From for Error { - fn from(inner: dns::WireError) -> Self { +impl From for Error { + fn from(inner: doge_dns::WireError) -> Self { Self::WireError(inner) } } diff --git a/dns-transport/src/https.rs b/dns-transport/src/https.rs index a68a532..7971d79 100644 --- a/dns-transport/src/https.rs +++ b/dns-transport/src/https.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use log::*; -use dns::{Request, Response, WireError}; +use doge_dns::{Request, Response, WireError}; use crate::GenericTransport; use super::{Transport, Error}; diff --git a/dns-transport/src/lib.rs b/dns-transport/src/lib.rs index 84f0ce5..e4ad83f 100644 --- a/dns-transport/src/lib.rs +++ b/dns-transport/src/lib.rs @@ -58,7 +58,7 @@ pub trait Transport { /// receiving data, or the DNS packet in the response contained invalid /// bytes and failed to parse, or if there was a protocol-level error for /// the TLS and HTTPS transports. - fn send(&self, request: &dns::Request) -> Result; + fn send(&self, request: &doge_dns::Request) -> Result; } /// The **Generic transport**, Allows easier passthrough of data to other transports diff --git a/dns-transport/src/tcp.rs b/dns-transport/src/tcp.rs index 854b0b2..be04da7 100644 --- a/dns-transport/src/tcp.rs +++ b/dns-transport/src/tcp.rs @@ -4,7 +4,7 @@ use std::io::{Read, Write}; use log::*; -use dns::{Request, Response}; +use doge_dns::{Request, Response}; use crate::GenericTransport; use super::{Transport, Error}; diff --git a/dns-transport/src/tls.rs b/dns-transport/src/tls.rs index 7fe571f..632eaa7 100644 --- a/dns-transport/src/tls.rs +++ b/dns-transport/src/tls.rs @@ -5,7 +5,7 @@ use std::io::Write; use log::*; -use dns::{Request, Response}; +use doge_dns::{Request, Response}; use crate::GenericTransport; use super::{Transport, Error, TcpTransport}; diff --git a/dns-transport/src/udp.rs b/dns-transport/src/udp.rs index e8d09b7..c415133 100644 --- a/dns-transport/src/udp.rs +++ b/dns-transport/src/udp.rs @@ -2,7 +2,7 @@ use std::net::{Ipv4Addr, UdpSocket}; use log::*; -use dns::{Request, Response}; +use doge_dns::{Request, Response}; use crate::GenericTransport; use super::{Transport, Error}; diff --git a/dns/Cargo.toml b/dns/Cargo.toml deleted file mode 100644 index aff2dee..0000000 --- a/dns/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "dns" -version = "0.2.4-beta" -author = ["Darrion Whitfield "] -# Original author authors = ["Benjamin Sago "] -edition = "2018" - -[lib] -doctest = false - - -[dependencies] - -# logging -log = "0.4" - -# protocol parsing helper -byteorder = "1.3" - -# printing of certain packets -base64 = "0.21" - -# idna encoding -unic-idna = { version = "0.9.0", optional = true } - -# mutation testing -mutagen = { git = "https://github.com/llogiq/mutagen", optional = true } - -[dev-dependencies] -pretty_assertions = "0.7" - -[features] -default = [] # idna is enabled in the main dog crate -with_idna = ["unic-idna"] -with_mutagen = ["mutagen"] # needs nightly diff --git a/dns/fuzz/.gitignore b/dns/fuzz/.gitignore deleted file mode 100644 index 572e03b..0000000 --- a/dns/fuzz/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ - -target -corpus -artifacts diff --git a/dns/fuzz/Cargo.lock b/dns/fuzz/Cargo.lock deleted file mode 100644 index 48caf6e..0000000 --- a/dns/fuzz/Cargo.lock +++ /dev/null @@ -1,62 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arbitrary" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cc" -version = "1.0.60" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "dns" -version = "0.1.0" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dns-fuzz" -version = "0.0.1" -dependencies = [ - "dns 0.1.0", - "libfuzzer-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "libfuzzer-sys" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arbitrary 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum arbitrary 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0922a3e746b5a44e111e5603feb6704e5cc959116f66737f50bb5cbd264e9d87" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cc 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)" = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum libfuzzer-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc" -"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" diff --git a/dns/fuzz/Cargo.toml b/dns/fuzz/Cargo.toml deleted file mode 100644 index 6df808e..0000000 --- a/dns/fuzz/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "dns-fuzz" -version = "0.0.1" -authors = ["Automatically generated"] -publish = false - -[package.metadata] -cargo-fuzz = true - -[dependencies.dns] -path = ".." - -[dependencies.libfuzzer-sys] -version = "0.3.0" - -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - -[[bin]] -name = "fuzz_parsing" -path = "fuzz_targets/fuzz_parsing.rs" diff --git a/dns/fuzz/fuzz_targets/fuzz_parsing.rs b/dns/fuzz/fuzz_targets/fuzz_parsing.rs deleted file mode 100644 index dbc05f3..0000000 --- a/dns/fuzz/fuzz_targets/fuzz_parsing.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![no_main] -#[macro_use] extern crate libfuzzer_sys; -extern crate dns; -use dns::Response; - -fuzz_target!(|data: &[u8]| { - let _ = Response::from_bytes(data); -}); diff --git a/dns/src/lib.rs b/dns/src/lib.rs deleted file mode 100644 index fe2d443..0000000 --- a/dns/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![warn(deprecated_in_future)] -#![warn(future_incompatible)] -#![warn(missing_copy_implementations)] -#![warn(missing_docs)] -#![warn(nonstandard_style)] -#![warn(rust_2018_compatibility)] -#![warn(rust_2018_idioms)] -#![warn(single_use_lifetimes)] -#![warn(trivial_casts, trivial_numeric_casts)] -#![warn(unused)] - -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::doc_markdown)] -#![allow(clippy::len_without_is_empty)] -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::must_use_candidate)] -#![allow(clippy::non_ascii_literal)] -#![allow(clippy::redundant_else)] -#![allow(clippy::struct_excessive_bools)] -#![allow(clippy::upper_case_acronyms)] -#![allow(clippy::wildcard_imports)] - -#![deny(clippy::cast_possible_truncation)] -#![deny(clippy::cast_lossless)] -#![deny(clippy::cast_possible_wrap)] -#![deny(clippy::cast_sign_loss)] -#![deny(unsafe_code)] - - -//! The DNS crate is the ‘library’ part of dog. It implements the DNS -//! protocol: creating and decoding packets from their byte structure. - - -mod types; -pub use self::types::*; - -mod strings; -pub use self::strings::Labels; - -mod wire; -pub use self::wire::{Wire, WireError, MandatedLength}; - -pub mod record; diff --git a/dns/src/record/a.rs b/dns/src/record/a.rs deleted file mode 100644 index 7500c1c..0000000 --- a/dns/src/record/a.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::net::Ipv4Addr; - -use log::*; - -use crate::wire::*; - - -/// An **A** record type, which contains an `Ipv4Address`. -/// -/// # References -/// -/// - [RFC 1035 §3.4.1](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct A { - - /// The IPv4 address contained in the packet. - pub address: Ipv4Addr, -} - -impl Wire for A { - const NAME: &'static str = "A"; - const RR_TYPE: u16 = 1; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - if stated_length != 4 { - warn!("Length is incorrect (record length {:?}, but should be four)", stated_length); - let mandated_length = MandatedLength::Exactly(4); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let mut buf = [0_u8; 4]; - c.read_exact(&mut buf)?; - - let address = Ipv4Addr::from(buf); - trace!("Parsed IPv4 address -> {:?}", address); - - Ok(Self { address }) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x7F, 0x00, 0x00, 0x01, // IPv4 address - ]; - - assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - A { address: Ipv4Addr::new(127, 0, 0, 1) }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x7F, 0x00, 0x00, // Too short IPv4 address - ]; - - assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(4) })); - } - - #[test] - fn record_too_long() { - let buf = &[ - 0x7F, 0x00, 0x00, 0x00, // IPv4 address - 0x01, // Unexpected extra byte - ]; - - assert_eq!(A::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 5, mandated_length: MandatedLength::Exactly(4) })); - } - - #[test] - fn record_empty() { - assert_eq!(A::read(0, &mut Cursor::new(&[])), - Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(4) })); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x7F, 0x00, // Half an IPv4 address - ]; - - assert_eq!(A::read(4, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/aaaa.rs b/dns/src/record/aaaa.rs deleted file mode 100644 index 64971f8..0000000 --- a/dns/src/record/aaaa.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::net::Ipv6Addr; - -use log::*; - -use crate::wire::*; - - -/// A **AAAA** record, which contains an `Ipv6Address`. -/// -/// # References -/// -/// - [RFC 3596](https://tools.ietf.org/html/rfc3596) — DNS Extensions to -/// Support IP Version 6 (October 2003) -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct AAAA { - - /// The IPv6 address contained in the packet. - pub address: Ipv6Addr, -} - -impl Wire for AAAA { - const NAME: &'static str = "AAAA"; - const RR_TYPE: u16 = 28; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - if stated_length != 16 { - warn!("Length is incorrect (stated length {:?}, but should be sixteen)", stated_length); - let mandated_length = MandatedLength::Exactly(16); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let mut buf = [0_u8; 16]; - c.read_exact(&mut buf)?; - - let address = Ipv6Addr::from(buf); - trace!("Parsed IPv6 address -> {:#x?}", address); - - Ok(Self { address }) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IPv6 address - ]; - - assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - AAAA { address: Ipv6Addr::new(0,0,0,0,0,0,0,0) }); - } - - #[test] - fn record_too_long() { - let buf = &[ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, // IPv6 address - 0x09, // Unexpected extra byte - ]; - - assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 17, mandated_length: MandatedLength::Exactly(16) })); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x05, 0x05, 0x05, 0x05, 0x05, // Five arbitrary bytes - ]; - - assert_eq!(AAAA::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 5, mandated_length: MandatedLength::Exactly(16) })); - } - - #[test] - fn record_empty() { - assert_eq!(AAAA::read(0, &mut Cursor::new(&[])), - Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(16) })); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x05, 0x05, 0x05, 0x05, 0x05, // Five arbitrary bytes - ]; - - assert_eq!(AAAA::read(16, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/caa.rs b/dns/src/record/caa.rs deleted file mode 100644 index 33f6aaf..0000000 --- a/dns/src/record/caa.rs +++ /dev/null @@ -1,134 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **CAA** _(certification authority authorization)_ record. These allow -/// domain names to specify which Certificate Authorities are allowed to issue -/// certificates for the domain. -/// -/// # References -/// -/// - [RFC 6844](https://tools.ietf.org/html/rfc6844) — DNS Certification -/// Authority Authorization Resource Record (January 2013) -#[derive(PartialEq, Debug)] -pub struct CAA { - - /// Whether this record is marked as “critical” or not. - pub critical: bool, - - /// The “tag” part of the CAA record. - pub tag: Box<[u8]>, - - /// The “value” part of the CAA record. - pub value: Box<[u8]>, -} - -impl Wire for CAA { - const NAME: &'static str = "CAA"; - const RR_TYPE: u16 = 257; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - - // flags - let flags = c.read_u8()?; - trace!("Parsed flags -> {:#08b}", flags); - - let has_bit = |bit| { flags & bit == bit }; - let critical = has_bit(0b_1000_0000); - trace!("Parsed critical flag -> {:?}", critical); - - // tag - let tag_length = c.read_u8()?; - trace!("Parsed tag length -> {:?}", tag_length); - - let mut tag = vec![0_u8; usize::from(tag_length)].into_boxed_slice(); - c.read_exact(&mut tag)?; - trace!("Parsed tag -> {:?}", String::from_utf8_lossy(&tag)); - - // value - let remaining_length = stated_length.saturating_sub(u16::from(tag_length)).saturating_sub(2); - trace!("Remaining length -> {:?}", remaining_length); - - let mut value = vec![0_u8; usize::from(remaining_length)].into_boxed_slice(); - c.read_exact(&mut value)?; - trace!("Parsed value -> {:?}", String::from_utf8_lossy(&value)); - - Ok(Self { critical, tag, value }) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses_non_critical() { - let buf = &[ - 0x00, // flags (all unset) - 0x09, // tag length - 0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64, // tag - 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, // value - ]; - - assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - CAA { - critical: false, - tag: Box::new(*b"issuewild"), - value: Box::new(*b"entrust.net"), - }); - } - - #[test] - fn parses_critical() { - let buf = &[ - 0x80, // flags (critical bit set) - 0x09, // tag length - 0x69, 0x73, 0x73, 0x75, 0x65, 0x77, 0x69, 0x6c, 0x64, // tag - 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, // value - ]; - - assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - CAA { - critical: true, - tag: Box::new(*b"issuewild"), - value: Box::new(*b"entrust.net"), - }); - } - - #[test] - fn ignores_other_flags() { - let buf = &[ - 0x7F, // flags (all except critical bit set) - 0x01, // tag length - 0x65, // tag - 0x45, // value - ]; - - assert_eq!(CAA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - CAA { - critical: false, - tag: Box::new(*b"e"), - value: Box::new(*b"E"), - }); - } - - #[test] - fn record_empty() { - assert_eq!(CAA::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, // flags - ]; - - assert_eq!(CAA::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/cname.rs b/dns/src/record/cname.rs deleted file mode 100644 index c1c81a0..0000000 --- a/dns/src/record/cname.rs +++ /dev/null @@ -1,86 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **CNAME** _(canonical name)_ record, which aliases one domain to another. -/// -/// # References -/// -/// - [RFC 1035 §3.3.1](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct CNAME { - - /// The domain name that this CNAME record is responding with. - pub domain: Labels, -} - -impl Wire for CNAME { - const NAME: &'static str = "CNAME"; - const RR_TYPE: u16 = 5; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let (domain, domain_length) = c.read_labels()?; - trace!("Parsed domain -> {:?}", domain); - - if stated_length == domain_length { - trace!("Length is correct"); - Ok(Self { domain }) - } - else { - warn!("Length is incorrect (stated length {:?}, domain length {:?})", stated_length, domain_length); - Err(WireError::WrongLabelLength { stated_length, length_after_labels: domain_length }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // domain - 0x00, // domain terminator - ]; - - assert_eq!(CNAME::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - CNAME { - domain: Labels::encode("bsago.me").unwrap(), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x03, 0x65, 0x66, 0x67, // domain - 0x00, // domain terminator - ]; - - assert_eq!(CNAME::read(6, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 5 })); - } - - #[test] - fn record_empty() { - assert_eq!(CNAME::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x05, 0x62, 0x73, // the stard of a string - ]; - - assert_eq!(CNAME::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} - diff --git a/dns/src/record/eui48.rs b/dns/src/record/eui48.rs deleted file mode 100644 index 253816f..0000000 --- a/dns/src/record/eui48.rs +++ /dev/null @@ -1,111 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **EUI48** record, which holds a six-octet (48-bit) Extended Unique -/// Identifier. These identifiers can be used as MAC addresses. -/// -/// # References -/// -/// - [RFC 7043](https://tools.ietf.org/html/rfc7043) — Resource Records for -/// EUI-48 and EUI-64 Addresses in the DNS (October 2013) -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct EUI48 { - - /// The six octets that make up the identifier. - pub octets: [u8; 6], -} - -impl Wire for EUI48 { - const NAME: &'static str = "EUI48"; - const RR_TYPE: u16 = 108; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - if stated_length != 6 { - warn!("Length is incorrect (record length {:?}, but should be six)", stated_length); - let mandated_length = MandatedLength::Exactly(6); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let mut octets = [0_u8; 6]; - c.read_exact(&mut octets)?; - trace!("Parsed 6-byte address -> {:#x?}", octets); - - Ok(Self { octets }) - } -} - - -impl EUI48 { - - /// Returns this EUI as hexadecimal numbers, separated by dashes. - pub fn formatted_address(self) -> String { - format!("{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", - self.octets[0], self.octets[1], self.octets[2], - self.octets[3], self.octets[4], self.octets[5]) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, // identifier - ]; - - assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - EUI48 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56 ] }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x00, 0x7F, 0x23, // a mere OUI - ]; - - assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(6) })); - } - - #[test] - fn record_too_long() { - let buf = &[ - 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, // identifier - 0x01, // an unexpected extra byte - ]; - - assert_eq!(EUI48::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 7, mandated_length: MandatedLength::Exactly(6) })); - } - - #[test] - fn record_empty() { - assert_eq!(EUI48::read(0, &mut Cursor::new(&[])), - Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(6) })); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, 0x7F, 0x23, // a mere OUI - ]; - - assert_eq!(EUI48::read(6, &mut Cursor::new(buf)), - Err(WireError::IO)); - } - - #[test] - fn hex_rep() { - let record = EUI48 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56 ] }; - - assert_eq!(record.formatted_address(), - "00-7f-23-12-34-56"); - } -} diff --git a/dns/src/record/eui64.rs b/dns/src/record/eui64.rs deleted file mode 100644 index c874b5b..0000000 --- a/dns/src/record/eui64.rs +++ /dev/null @@ -1,111 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **EUI64** record, which holds an eight-octet (64-bit) Extended Unique -/// Identifier. -/// -/// # References -/// -/// - [RFC 7043](https://tools.ietf.org/html/rfc7043) — Resource Records for -/// EUI-48 and EUI-64 Addresses in the DNS (October 2013) -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct EUI64 { - - /// The eight octets that make up the identifier. - pub octets: [u8; 8], -} - -impl Wire for EUI64 { - const NAME: &'static str = "EUI64"; - const RR_TYPE: u16 = 109; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - if stated_length != 8 { - warn!("Length is incorrect (record length {:?}, but should be eight)", stated_length); - let mandated_length = MandatedLength::Exactly(8); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let mut octets = [0_u8; 8]; - c.read_exact(&mut octets)?; - trace!("Parsed 8-byte address -> {:#x?}", octets); - - Ok(Self { octets }) - } -} - - -impl EUI64 { - - /// Returns this EUI as hexadecimal numbers, separated by dashes. - pub fn formatted_address(self) -> String { - format!("{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", - self.octets[0], self.octets[1], self.octets[2], self.octets[3], - self.octets[4], self.octets[5], self.octets[6], self.octets[7]) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90, // identifier - ]; - - assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - EUI64 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90 ] }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x00, 0x7F, 0x23, // a mere OUI - ]; - - assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::Exactly(8) })); - } - - #[test] - fn record_too_long() { - let buf = &[ - 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90, // identifier - 0x01, // an unexpected extra byte - ]; - - assert_eq!(EUI64::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 9, mandated_length: MandatedLength::Exactly(8) })); - } - - #[test] - fn record_empty() { - assert_eq!(EUI64::read(0, &mut Cursor::new(&[])), - Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::Exactly(8) })); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, 0x7F, 0x23, // a mere OUI - ]; - - assert_eq!(EUI64::read(8, &mut Cursor::new(buf)), - Err(WireError::IO)); - } - - #[test] - fn hex_rep() { - let record = EUI64 { octets: [ 0x00, 0x7F, 0x23, 0x12, 0x34, 0x56, 0x78, 0x90 ] }; - - assert_eq!(record.formatted_address(), - "00-7f-23-12-34-56-78-90"); - } -} diff --git a/dns/src/record/hinfo.rs b/dns/src/record/hinfo.rs deleted file mode 100644 index 3e7c750..0000000 --- a/dns/src/record/hinfo.rs +++ /dev/null @@ -1,112 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A (an?) **HINFO** _(host information)_ record, which contains the CPU and -/// OS information about a host. -/// -/// It also gets used as the response for an `ANY` query, if it is blocked. -/// -/// # References -/// -/// - [RFC 1035 §3.3.2](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -/// - [RFC 8482 §6](https://tools.ietf.org/html/rfc8482#section-6) — Providing -/// Minimal-Sized Responses to DNS Queries That Have QTYPE=ANY (January 2019) -#[derive(PartialEq, Debug)] -pub struct HINFO { - - /// The CPU field, specifying the CPU type. - pub cpu: Box<[u8]>, - - /// The OS field, specifying the operating system. - pub os: Box<[u8]>, -} - -impl Wire for HINFO { - const NAME: &'static str = "HINFO"; - const RR_TYPE: u16 = 13; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - - let cpu_length = c.read_u8()?; - trace!("Parsed CPU length -> {:?}", cpu_length); - - let mut cpu = vec![0_u8; usize::from(cpu_length)].into_boxed_slice(); - c.read_exact(&mut cpu)?; - trace!("Parsed CPU -> {:?}", String::from_utf8_lossy(&cpu)); - - let os_length = c.read_u8()?; - trace!("Parsed OS length -> {:?}", os_length); - - let mut os = vec![0_u8; usize::from(os_length)].into_boxed_slice(); - c.read_exact(&mut os)?; - trace!("Parsed OS -> {:?}", String::from_utf8_lossy(&os)); - - let length_after_labels = 1 + u16::from(cpu_length) + 1 + u16::from(os_length); - if stated_length == length_after_labels { - trace!("Length is correct"); - Ok(Self { cpu, os }) - } - else { - warn!("Length is incorrect (stated length {:?}, cpu plus length {:?}", stated_length, length_after_labels); - Err(WireError::WrongLabelLength { stated_length, length_after_labels }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x0e, // cpu length - 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d, - 0x63, 0x70, 0x75, // cpu - 0x0d, // os length - 0x73, 0x6f, 0x6d, 0x65, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x61, 0x2d, - 0x6f, 0x73, // os - ]; - - assert_eq!(HINFO::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - HINFO { - cpu: Box::new(*b"some-kinda-cpu"), - os: Box::new(*b"some-kinda-os"), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x03, // cpu length - 0x65, 0x66, 0x67, // cpu - 0x03, // os length - 0x68, 0x69, 0x70, // os - ]; - - assert_eq!(HINFO::read(6, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 8 })); - } - - #[test] - fn record_empty() { - assert_eq!(HINFO::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x14, 0x0A, 0x0B, 0x0C, // 32-bit CPU - ]; - - assert_eq!(HINFO::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/loc.rs b/dns/src/record/loc.rs deleted file mode 100644 index 8b01e4f..0000000 --- a/dns/src/record/loc.rs +++ /dev/null @@ -1,493 +0,0 @@ -use std::fmt; - -use log::*; - -use crate::wire::*; - - -/// A **LOC** _(location)_ record, which points to a location on Earth using -/// its latitude, longitude, and altitude. -/// -/// # References -/// -/// - [RFC 1876](https://tools.ietf.org/html/rfc1876) — A Means for Expressing -/// Location Information in the Domain Name System (January 1996) -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct LOC { - - /// The diameter of a sphere enclosing the entity at the location, as a - /// measure of its size, measured in centimetres. - pub size: Size, - - /// The diameter of the “circle of error” that this location could be in, - /// measured in centimetres. - pub horizontal_precision: u8, - - /// The amount of vertical space that this location could be in, measured - /// in centimetres. - pub vertical_precision: u8, - - /// The latitude of the centre of the sphere. If `None`, the packet - /// parses, but the position is out of range. - pub latitude: Option, - - /// The longitude of the centre of the sphere. If `None`, the packet - /// parses, but the position is out of range. - pub longitude: Option, - - /// The altitude of the centre of the sphere, measured in centimetres - /// above a base of 100,000 metres below the GPS reference spheroid. - pub altitude: Altitude, -} - -/// A measure of size, in centimetres, represented by a base and an exponent. -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct Size { - base: u8, - power_of_ten: u8, -} - -/// A position on one of the world’s axes. -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct Position { - degrees: u32, - arcminutes: u32, - arcseconds: u32, - milliarcseconds: u32, - direction: Direction, -} - -/// A position on the vertical axis. -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct Altitude { - metres: i64, - centimetres: i64, -} - -/// One of the directions a position could be in, relative to the equator or -/// prime meridian. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum Direction { - North, - East, - South, - West, -} - -impl Wire for LOC { - const NAME: &'static str = "LOC"; - const RR_TYPE: u16 = 29; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let version = c.read_u8()?; - trace!("Parsed version -> {:?}", version); - - if version != 0 { - return Err(WireError::WrongVersion { - stated_version: version, - maximum_supported_version: 0, - }); - } - - if stated_length != 16 { - let mandated_length = MandatedLength::Exactly(16); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let size_bits = c.read_u8()?; - let size = Size::from_u8(size_bits); - trace!("Parsed size -> {:#08b} ({})", size_bits, size); - - let horizontal_precision = c.read_u8()?; - trace!("Parsed horizontal precision -> {:?}", horizontal_precision); - - let vertical_precision = c.read_u8()?; - trace!("Parsed vertical precision -> {:?}", vertical_precision); - - let latitude_num = c.read_u32::()?; - let latitude = Position::from_u32(latitude_num, true); - trace!("Parsed latitude -> {:?} ({:?})", latitude_num, latitude); - - let longitude_num = c.read_u32::()?; - let longitude = Position::from_u32(longitude_num, false); - trace!("Parsed longitude -> {:?} ({:?})", longitude_num, longitude); - - let altitude_num = c.read_u32::()?; - let altitude = Altitude::from_u32(altitude_num); - trace!("Parsed altitude -> {:?} ({:})", altitude_num, altitude); - - Ok(Self { - size, horizontal_precision, vertical_precision, latitude, longitude, altitude, - }) - } -} - -impl Size { - - /// Converts a number into the size it represents. To allow both small and - /// large sizes, the input octet is split into two four-bit sizes, one the - /// base, and one the power of ten exponent. - fn from_u8(input: u8) -> Self { - let base = input >> 4; - let power_of_ten = input & 0b_0000_1111; - Self { base, power_of_ten } - } -} - -impl Position { - - /// Converts a number into the position it represents. The input number is - /// measured in thousandths of an arcsecond (milliarcseconds), with 2^31 - /// as the equator or prime meridian. - /// - /// Returns `None` if the input is out of range, meaning it would wrap - /// around to another half of the Earth once or more. - fn from_u32(mut input: u32, vertical: bool) -> Option { - let max_for_direction = if vertical { 90 } else { 180 }; - let limit = 1000 * 60 * 60 * max_for_direction; - - if input < (0x_8000_0000 - limit) || input > (0x_8000_0000 + limit) { - // Input is out of range - None - } - else if input >= 0x_8000_0000 { - // Input is north or east, so de-relativise it and divide into segments - input -= 0x_8000_0000; - let milliarcseconds = input % 1000; - let total_arcseconds = input / 1000; - - let arcseconds = total_arcseconds % 60; - let total_arcminutes = total_arcseconds / 60; - - let arcminutes = total_arcminutes % 60; - let degrees = total_arcminutes / 60; - - let direction = if vertical { Direction::North } - else { Direction::East }; - - Some(Self { degrees, arcminutes, arcseconds, milliarcseconds, direction }) - } - else { - // Input is south or west, so do the calculations for - let mut pos = Self::from_u32(input + (0x_8000_0000_u32 - input) * 2, vertical)?; - - pos.direction = if vertical { Direction::South } - else { Direction::West }; - Some(pos) - } - } -} - -impl Altitude { - fn from_u32(input: u32) -> Self { - let mut input = i64::from(input); - input -= 10_000_000; // 100,000m - let metres = input / 100; - let centimetres = input % 100; - Self { metres, centimetres } - } -} - - -impl fmt::Display for Size { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}e{}", self.base, self.power_of_ten) - } -} - -impl fmt::Display for Position { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}°{}′{}", - self.degrees, - self.arcminutes, - self.arcseconds, - )?; - - if self.milliarcseconds != 0 { - write!(f, ".{:03}", self.milliarcseconds)?; - } - - write!(f, "″ {}", self.direction) - } -} - -impl fmt::Display for Direction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::North => write!(f, "N"), - Self::East => write!(f, "E"), - Self::South => write!(f, "S"), - Self::West => write!(f, "W"), - } - } -} - -impl fmt::Display for Altitude { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Usually there’s a space between the number and the unit, but - // spaces are already used to delimit segments in the record summary - if self.centimetres == 0 { - write!(f, "{}m", self.metres) - } - else { - write!(f, "{}.{:02}m", self.metres, self.centimetres) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, // version - 0x32, // size, - 0x00, // horizontal precision - 0x00, // vertical precision - 0x8b, 0x0d, 0x2c, 0x8c, // latitude - 0x7f, 0xf8, 0xfc, 0xa5, // longitude - 0x00, 0x98, 0x96, 0x80, // altitude - ]; - - assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - LOC { - size: Size { base: 3, power_of_ten: 2 }, - horizontal_precision: 0, - vertical_precision: 0, - latitude: Position::from_u32(0x_8b_0d_2c_8c, true), - longitude: Position::from_u32(0x_7f_f8_fc_a5, false), - altitude: Altitude::from_u32(0x_00_98_96_80), - }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x00, // version - 0x00, // size - ]; - - assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 2, mandated_length: MandatedLength::Exactly(16) })); - } - - #[test] - fn record_too_long() { - let buf = &[ - 0x00, // version - 0x32, // size, - 0x00, // horizontal precision - 0x00, // vertical precision - 0x8b, 0x0d, 0x2c, 0x8c, // latitude - 0x7f, 0xf8, 0xfc, 0xa5, // longitude - 0x00, 0x98, 0x96, 0x80, // altitude - 0x12, 0x34, 0x56, // some other stuff - ]; - - assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 19, mandated_length: MandatedLength::Exactly(16) })); - } - - #[test] - fn more_recent_version() { - let buf = &[ - 0x80, // version - 0x12, 0x34, 0x56, // some data in an unknown format - ]; - - assert_eq!(LOC::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongVersion { stated_version: 128, maximum_supported_version: 0 })); - } - - #[test] - fn record_empty() { - assert_eq!(LOC::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, // version - ]; - - assert_eq!(LOC::read(16, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} - - -#[cfg(test)] -mod size_test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn zeroes() { - assert_eq!(Size::from_u8(0b_0000_0000).to_string(), - String::from("0e0")); - } - - #[test] - fn ones() { - assert_eq!(Size::from_u8(0b_0001_0001).to_string(), - String::from("1e1")); - } - - #[test] - fn schfourteen_teen() { - assert_eq!(Size::from_u8(0b_1110_0011).to_string(), - String::from("14e3")); - } - - #[test] - fn ones_but_bits_this_time() { - assert_eq!(Size::from_u8(0b_1111_1111).to_string(), - String::from("15e15")); - } -} - - -#[cfg(test)] -mod position_test { - use super::*; - use pretty_assertions::assert_eq; - - // centre line tests - - #[test] - fn meridian() { - assert_eq!(Position::from_u32(0x_8000_0000, false).unwrap().to_string(), - String::from("0°0′0″ E")); - } - - #[test] - fn meridian_plus_one() { - assert_eq!(Position::from_u32(0x_8000_0000 + 1, false).unwrap().to_string(), - String::from("0°0′0.001″ E")); - } - - #[test] - fn meridian_minus_one() { - assert_eq!(Position::from_u32(0x_8000_0000 - 1, false).unwrap().to_string(), - String::from("0°0′0.001″ W")); - } - - #[test] - fn equator() { - assert_eq!(Position::from_u32(0x_8000_0000, true).unwrap().to_string(), - String::from("0°0′0″ N")); - } - - #[test] - fn equator_plus_one() { - assert_eq!(Position::from_u32(0x_8000_0000 + 1, true).unwrap().to_string(), - String::from("0°0′0.001″ N")); - } - - #[test] - fn equator_minus_one() { - assert_eq!(Position::from_u32(0x_8000_0000 - 1, true).unwrap().to_string(), - String::from("0°0′0.001″ S")); - } - - // arbitrary value tests - - #[test] - fn some_latitude() { - assert_eq!(Position::from_u32(2332896396, true).unwrap().to_string(), - String::from("51°30′12.748″ N")); - } - - #[test] - fn some_longitude() { - assert_eq!(Position::from_u32(2147024037, false).unwrap().to_string(), - String::from("0°7′39.611″ W")); - } - - // limit tests - - #[test] - fn the_north_pole() { - assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 90), true).unwrap().to_string(), - String::from("90°0′0″ N")); - } - - #[test] - fn the_north_pole_plus_one() { - assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 90) + 1, true), - None); - } - - #[test] - fn the_south_pole() { - assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 90), true).unwrap().to_string(), - String::from("90°0′0″ S")); - } - - #[test] - fn the_south_pole_minus_one() { - assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 90) - 1, true), - None); - } - - #[test] - fn the_far_east() { - assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 180), false).unwrap().to_string(), - String::from("180°0′0″ E")); - } - - #[test] - fn the_far_east_plus_one() { - assert_eq!(Position::from_u32(0x8000_0000 + (1000 * 60 * 60 * 180) + 1, false), - None); - } - - #[test] - fn the_far_west() { - assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 180), false).unwrap().to_string(), - String::from("180°0′0″ W")); - } - - #[test] - fn the_far_west_minus_one() { - assert_eq!(Position::from_u32(0x8000_0000 - (1000 * 60 * 60 * 180) - 1, false), - None); - } -} - - -#[cfg(test)] -mod altitude_test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn base_level() { - assert_eq!(Altitude::from_u32(10000000).to_string(), - String::from("0m")); - } - - #[test] - fn up_high() { - assert_eq!(Altitude::from_u32(20000000).to_string(), - String::from("100000m")); - } - - #[test] - fn down_low() { - assert_eq!(Altitude::from_u32(0).to_string(), - String::from("-100000m")); - } - - #[test] - fn with_decimal() { - assert_eq!(Altitude::from_u32(50505050).to_string(), - String::from("405050.50m")); - } -} diff --git a/dns/src/record/mod.rs b/dns/src/record/mod.rs deleted file mode 100644 index 908fb4d..0000000 --- a/dns/src/record/mod.rs +++ /dev/null @@ -1,238 +0,0 @@ -//! All the DNS record types, as well as how to parse each type. - -use crate::wire::*; - - -mod a; -pub use self::a::A; - -mod aaaa; -pub use self::aaaa::AAAA; - -mod caa; -pub use self::caa::CAA; - -mod cname; -pub use self::cname::CNAME; - -mod eui48; -pub use self::eui48::EUI48; - -mod eui64; -pub use self::eui64::EUI64; - -mod hinfo; -pub use self::hinfo::HINFO; - -mod loc; -pub use self::loc::LOC; - -mod mx; -pub use self::mx::MX; - -mod naptr; -pub use self::naptr::NAPTR; - -mod ns; -pub use self::ns::NS; - -mod openpgpkey; -pub use self::openpgpkey::OPENPGPKEY; - -mod opt; -pub use self::opt::OPT; - -mod ptr; -pub use self::ptr::PTR; - -mod sshfp; -pub use self::sshfp::SSHFP; - -mod soa; -pub use self::soa::SOA; - -mod srv; -pub use self::srv::SRV; - -mod tlsa; -pub use self::tlsa::TLSA; - -mod txt; -pub use self::txt::TXT; - -mod uri; -pub use self::uri::URI; - - -mod others; -pub use self::others::UnknownQtype; - - -/// A record that’s been parsed from a byte buffer. -#[derive(PartialEq, Debug)] -#[allow(missing_docs)] -pub enum Record { - A(A), - AAAA(AAAA), - CAA(CAA), - CNAME(CNAME), - EUI48(EUI48), - EUI64(EUI64), - HINFO(HINFO), - LOC(LOC), - MX(MX), - NAPTR(NAPTR), - NS(NS), - OPENPGPKEY(OPENPGPKEY), - // OPT is not included here. - PTR(PTR), - SSHFP(SSHFP), - SOA(SOA), - SRV(SRV), - TLSA(TLSA), - TXT(TXT), - URI(URI), - - /// A record with a type that we don’t recognise. - Other { - - /// The number that’s meant to represent the record type. - type_number: UnknownQtype, - - /// The undecodable bytes that were in this record. - bytes: Vec, - }, -} - - -/// The type of a record that may or may not be one of the known ones. Has no -/// data associated with it other than what type of record it is. -#[derive(PartialEq, Debug, Copy, Clone)] -#[allow(missing_docs)] -pub enum RecordType { - A, - AAAA, - CAA, - CNAME, - EUI48, - EUI64, - HINFO, - LOC, - MX, - NAPTR, - NS, - OPENPGPKEY, - PTR, - SSHFP, - SOA, - SRV, - TLSA, - TXT, - URI, - - /// A record type we don’t recognise. - Other(UnknownQtype), -} - -impl From for RecordType { - fn from(type_number: u16) -> Self { - macro_rules! try_record { - ($record:tt) => { - if $record::RR_TYPE == type_number { - return RecordType::$record; - } - } - } - - try_record!(A); - try_record!(AAAA); - try_record!(CAA); - try_record!(CNAME); - try_record!(EUI48); - try_record!(EUI64); - try_record!(HINFO); - try_record!(LOC); - try_record!(MX); - try_record!(NAPTR); - try_record!(NS); - try_record!(OPENPGPKEY); - // OPT is handled separately - try_record!(PTR); - try_record!(SSHFP); - try_record!(SOA); - try_record!(SRV); - try_record!(TLSA); - try_record!(TXT); - try_record!(URI); - - RecordType::Other(UnknownQtype::from(type_number)) - } -} - - -impl RecordType { - - /// Determines the record type with a given name, or `None` if none is - /// known. Matches names case-insensitively. - pub fn from_type_name(type_name: &str) -> Option { - macro_rules! try_record { - ($record:tt) => { - if $record::NAME.eq_ignore_ascii_case(type_name) { - return Some(Self::$record); - } - } - } - - try_record!(A); - try_record!(AAAA); - try_record!(CAA); - try_record!(CNAME); - try_record!(EUI48); - try_record!(EUI64); - try_record!(HINFO); - try_record!(LOC); - try_record!(MX); - try_record!(NAPTR); - try_record!(NS); - try_record!(OPENPGPKEY); - // OPT is elsewhere - try_record!(PTR); - try_record!(SSHFP); - try_record!(SOA); - try_record!(SRV); - try_record!(TLSA); - try_record!(TXT); - try_record!(URI); - - UnknownQtype::from_type_name(type_name).map(Self::Other) - } - - /// Returns the record type number associated with this record type. - pub fn type_number(self) -> u16 { - match self { - Self::A => A::RR_TYPE, - Self::AAAA => AAAA::RR_TYPE, - Self::CAA => CAA::RR_TYPE, - Self::CNAME => CNAME::RR_TYPE, - Self::EUI48 => EUI48::RR_TYPE, - Self::EUI64 => EUI64::RR_TYPE, - Self::HINFO => HINFO::RR_TYPE, - Self::LOC => LOC::RR_TYPE, - Self::MX => MX::RR_TYPE, - Self::NAPTR => NAPTR::RR_TYPE, - Self::NS => NS::RR_TYPE, - Self::OPENPGPKEY => OPENPGPKEY::RR_TYPE, - // Wherefore art thou, OPT - Self::PTR => PTR::RR_TYPE, - Self::SSHFP => SSHFP::RR_TYPE, - Self::SOA => SOA::RR_TYPE, - Self::SRV => SRV::RR_TYPE, - Self::TLSA => TLSA::RR_TYPE, - Self::TXT => TXT::RR_TYPE, - Self::URI => URI::RR_TYPE, - Self::Other(o) => o.type_number(), - } - } -} - -// This code is really repetitive, I know, I know diff --git a/dns/src/record/mx.rs b/dns/src/record/mx.rs deleted file mode 100644 index 86ea7e9..0000000 --- a/dns/src/record/mx.rs +++ /dev/null @@ -1,97 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// An **MX** _(mail exchange)_ record, which contains the hostnames for mail -/// servers that handle mail sent to the domain. -/// -/// # References -/// -/// - [RFC 1035 §3.3.9](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct MX { - - /// The preference that clients should give to this MX record amongst all - /// that get returned. - pub preference: u16, - - /// The domain name of the mail exchange server. - pub exchange: Labels, -} - -impl Wire for MX { - const NAME: &'static str = "MX"; - const RR_TYPE: u16 = 15; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let preference = c.read_u16::()?; - trace!("Parsed preference -> {:?}", preference); - - let (exchange, exchange_length) = c.read_labels()?; - trace!("Parsed exchange -> {:?}", exchange); - - let length_after_labels = 2 + exchange_length; - if stated_length == length_after_labels { - trace!("Length is correct"); - Ok(Self { preference, exchange }) - } - else { - warn!("Length is incorrect (stated length {:?}, preference plus exchange length {:?}", stated_length, length_after_labels); - Err(WireError::WrongLabelLength { stated_length, length_after_labels }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x0A, // preference - 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // exchange - 0x00, // exchange terminator - ]; - - assert_eq!(MX::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - MX { - preference: 10, - exchange: Labels::encode("bsago.me").unwrap(), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x00, 0x0A, // preference - 0x03, 0x65, 0x66, 0x67, // domain - 0x00, // domain terminator - ]; - - assert_eq!(MX::read(6, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 7 })); - } - - #[test] - fn record_empty() { - assert_eq!(MX::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, 0x0A, // half a preference - ]; - - assert_eq!(MX::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/naptr.rs b/dns/src/record/naptr.rs deleted file mode 100644 index fac7dfd..0000000 --- a/dns/src/record/naptr.rs +++ /dev/null @@ -1,160 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **NAPTR** _(naming authority pointer)_ record, which holds a rule for -/// the Dynamic Delegation Discovery System. -/// -/// # References -/// -/// - [RFC 3403](https://tools.ietf.org/html/rfc3403) — Dynamic Delegation -/// Discovery System (DDDS) Part Three: The Domain Name System (DNS) Database -/// (October 2002) -#[derive(PartialEq, Debug)] -pub struct NAPTR { - - /// The order in which NAPTR records must be processed. - pub order: u16, - - /// The DDDS priority. - pub preference: u16, - - /// A set of characters that control the rewriting and interpretation of - /// the other fields. - pub flags: Box<[u8]>, - - /// The service parameters applicable to this delegation path. - pub service: Box<[u8]>, - - /// A regular expression that gets applied to a string in order to - /// construct the next domain name to look up using the DDDS algorithm. - pub regex: Box<[u8]>, - - /// The replacement domain name as part of the DDDS algorithm. - pub replacement: Labels, -} - -impl Wire for NAPTR { - const NAME: &'static str = "NAPTR"; - const RR_TYPE: u16 = 35; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let order = c.read_u16::()?; - trace!("Parsed order -> {:?}", order); - - // preference - let preference = c.read_u16::()?; - trace!("Parsed preference -> {:?}", preference); - - // flags - let flags_length = c.read_u8()?; - trace!("Parsed flags length -> {:?}", flags_length); - - let mut flags = vec![0_u8; usize::from(flags_length)].into_boxed_slice(); - c.read_exact(&mut flags)?; - trace!("Parsed flags -> {:?}", String::from_utf8_lossy(&flags)); - - // service - let service_length = c.read_u8()?; - trace!("Parsed service length -> {:?}", service_length); - - let mut service = vec![0_u8; usize::from(service_length)].into_boxed_slice(); - c.read_exact(&mut service)?; - trace!("Parsed service -> {:?}", String::from_utf8_lossy(&service)); - - // regex - let regex_length = c.read_u8()?; - trace!("Parsed regex length -> {:?}", regex_length); - - let mut regex = vec![0_u8; usize::from(regex_length)].into_boxed_slice(); - c.read_exact(&mut regex)?; - trace!("Parsed regex -> {:?}", String::from_utf8_lossy(®ex)); - - // replacement - let (replacement, replacement_length) = c.read_labels()?; - trace!("Parsed replacement -> {:?}", replacement); - - let length_after_labels = 2 + 2 + - 1 + u16::from(flags_length) + 1 + u16::from(service_length) + - 1 + u16::from(regex_length) + replacement_length; - - if stated_length == length_after_labels { - Ok(Self { order, preference, flags, service, regex, replacement }) - } - else { - Err(WireError::WrongLabelLength { stated_length, length_after_labels }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x05, // order - 0x00, 0x0a, // preference - 0x01, // flags length - 0x73, // flags - 0x03, // service length - 0x53, 0x52, 0x56, // service - 0x0e, // regex length - 0x5c, 0x64, 0x5c, 0x64, 0x3a, 0x5c, 0x64, 0x5c, 0x64, 0x3a, 0x5c, - 0x64, 0x5c, 0x64, // regex - 0x0b, 0x73, 0x72, 0x76, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f, - 0x67, 0x00, // replacement - ]; - - assert_eq!(NAPTR::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - NAPTR { - order: 5, - preference: 10, - flags: Box::new(*b"s"), - service: Box::new(*b"SRV"), - regex: Box::new(*b"\\d\\d:\\d\\d:\\d\\d"), - replacement: Labels::encode("srv-example.lookup.dog").unwrap(), - }); - } - - #[test] - fn incorrect_length() { - let buf = &[ - 0x00, 0x05, // order - 0x00, 0x0a, // preference - 0x01, // flags length - 0x73, // flags - 0x03, // service length - 0x53, 0x52, 0x56, // service - 0x01, // regex length - 0x64, // regex, - 0x00, // replacement - ]; - - assert_eq!(NAPTR::read(11, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 11, length_after_labels: 13 })); - } - - #[test] - fn record_empty() { - assert_eq!(NAPTR::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, 0x0A, // order - ]; - - assert_eq!(NAPTR::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/ns.rs b/dns/src/record/ns.rs deleted file mode 100644 index 67cb978..0000000 --- a/dns/src/record/ns.rs +++ /dev/null @@ -1,87 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **NS** _(name server)_ record, which is used to point domains to name -/// servers. -/// -/// # References -/// -/// - [RFC 1035 §3.3.11](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct NS { - - /// The address of a nameserver that provides this DNS response. - pub nameserver: Labels, -} - -impl Wire for NS { - const NAME: &'static str = "NS"; - const RR_TYPE: u16 = 2; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let (nameserver, nameserver_length) = c.read_labels()?; - trace!("Parsed nameserver -> {:?}", nameserver); - - if stated_length == nameserver_length { - trace!("Length is correct"); - Ok(Self { nameserver }) - } - else { - warn!("Length is incorrect (stated length {:?}, nameserver length {:?}", stated_length, nameserver_length); - Err(WireError::WrongLabelLength { stated_length, length_after_labels: nameserver_length }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x01, 0x61, 0x0c, 0x67, 0x74, 0x6c, 0x64, 0x2d, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x03, 0x6e, 0x65, 0x74, // nameserver - 0x00, // nameserver terminator - ]; - - assert_eq!(NS::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - NS { - nameserver: Labels::encode("a.gtld-servers.net").unwrap(), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x03, 0x65, 0x66, 0x67, // nameserver - 0x00, // nameserver terminator - ]; - - assert_eq!(NS::read(66, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 66, length_after_labels: 5 })); - } - - #[test] - fn record_empty() { - assert_eq!(NS::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x01, // the first byte of a string - ]; - - assert_eq!(NS::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/openpgpkey.rs b/dns/src/record/openpgpkey.rs deleted file mode 100644 index 6fccb45..0000000 --- a/dns/src/record/openpgpkey.rs +++ /dev/null @@ -1,91 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **OPENPGPKEY** record, which holds a PGP key. -/// -/// # References -/// -/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc7929) — DNS-Based -/// Authentication of Named Entities Bindings for OpenPGP (August 2016) -#[derive(PartialEq, Debug)] -pub struct OPENPGPKEY { - - /// The PGP key, as unencoded bytes. - pub key: Vec, -} - -impl Wire for OPENPGPKEY { - const NAME: &'static str = "OPENPGPKEY"; - const RR_TYPE: u16 = 61; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - if stated_length == 0 { - let mandated_length = MandatedLength::AtLeast(1); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let mut key = vec![0_u8; usize::from(stated_length)]; - c.read_exact(&mut key)?; - trace!("Parsed key -> {:#x?}", key); - - Ok(Self { key }) - } -} - -impl OPENPGPKEY { - - /// The base64-encoded PGP key. - pub fn base64_key(&self) -> String { - base64::encode(&self.key) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x12, 0x34, 0x56, 0x78, // key - ]; - - assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - OPENPGPKEY { - key: vec![ 0x12, 0x34, 0x56, 0x78 ], - }); - } - - #[test] - fn one_byte_of_uri() { - let buf = &[ - 0x2b, // one byte of key - ]; - - assert_eq!(OPENPGPKEY::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - OPENPGPKEY { - key: vec![ 0x2b ], - }); - } - - #[test] - fn record_empty() { - assert_eq!(OPENPGPKEY::read(0, &mut Cursor::new(&[])), - Err(WireError::WrongRecordLength { stated_length: 0, mandated_length: MandatedLength::AtLeast(1) })); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x12, 0x34, // the beginning of a key - ]; - - assert_eq!(OPENPGPKEY::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/opt.rs b/dns/src/record/opt.rs deleted file mode 100644 index 5876d83..0000000 --- a/dns/src/record/opt.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::convert::TryFrom; -use std::io; - -use log::*; - -use crate::wire::*; - - -/// A **OPT** _(options)_ pseudo-record, which is used to extend the DNS -/// protocol with additional flags such as DNSSEC stuff. -/// -/// # Pseudo-record? -/// -/// Unlike all the other record types, which are used to return data about a -/// domain name, the OPT record type is used to add more options to the -/// request, including data about the client or the server. It can exist, with -/// a payload, as a query or a response, though it’s usually encountered in -/// the Additional section. Its purpose is to add more room to the DNS wire -/// format, as backwards compatibility makes it impossible to simply add more -/// flags to the header. -/// -/// The fact that this isn’t a standard record type is annoying for a DNS -/// implementation. It re-purposes the ‘class’ and ‘TTL’ fields of the -/// `Answer` struct, as they only have meaning when associated with a domain -/// name. This means that the parser has to treat the OPT type specially, -/// switching to `Opt::read` as soon as the rtype is detected. It also means -/// the output has to deal with missing classes and TTLs. -/// -/// # References -/// -/// - [RFC 6891](https://tools.ietf.org/html/rfc6891) — Extension Mechanisms -/// for DNS (April 2013) -#[derive(PartialEq, Debug, Clone)] -pub struct OPT { - - /// The maximum size of a UDP packet that the client supports. - pub udp_payload_size: u16, - - /// The bits that form an extended rcode when non-zero. - pub higher_bits: u8, - - /// The version number of the DNS extension mechanism. - pub edns0_version: u8, - - /// Sixteen bits worth of flags. - pub flags: u16, - - /// The payload of the OPT record. - pub data: Vec, -} - -impl OPT { - - /// The record type number associated with OPT. - pub const RR_TYPE: u16 = 41; - - /// Reads from the given cursor to parse an OPT record. - /// - /// The buffer will have slightly more bytes to read for an OPT record - /// than for a typical one: we will not have encountered the ‘class’ or - /// ‘ttl’ fields, which have different meanings for this record type. - /// See §6.1.3 of the RFC, “OPT Record TTL Field Use”. - /// - /// Unlike the `Wire::read` function, this does not require a length. - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - pub fn read(c: &mut Cursor<&[u8]>) -> Result { - let udp_payload_size = c.read_u16::()?; // replaces the class field - trace!("Parsed UDP payload size -> {:?}", udp_payload_size); - - let higher_bits = c.read_u8()?; // replaces the ttl field... - trace!("Parsed higher bits -> {:#08b}", higher_bits); - - let edns0_version = c.read_u8()?; // ...as does this... - trace!("Parsed EDNS(0) version -> {:?}", edns0_version); - - let flags = c.read_u16::()?; // ...as does this - trace!("Parsed flags -> {:#08b}", flags); - - let data_length = c.read_u16::()?; - trace!("Parsed data length -> {:?}", data_length); - - let mut data = vec![0_u8; usize::from(data_length)]; - c.read_exact(&mut data)?; - trace!("Parsed data -> {:#x?}", data); - - Ok(Self { udp_payload_size, higher_bits, edns0_version, flags, data }) - } - - /// Serialises this OPT record into a vector of bytes. - /// - /// This is necessary for OPT records to be sent in the Additional section - /// of requests. - pub fn to_bytes(&self) -> io::Result> { - let mut bytes = Vec::with_capacity(32); - - bytes.write_u16::(self.udp_payload_size)?; - bytes.write_u8(self.higher_bits)?; - bytes.write_u8(self.edns0_version)?; - bytes.write_u16::(self.flags)?; - - // We should not be sending any data at all in the request, really, - // so sending too much data is downright nonsensical - let data_len = u16::try_from(self.data.len()).expect("Sending too much data"); - bytes.write_u16::(data_len)?; - - for b in &self.data { - bytes.write_u8(*b)?; - } - - Ok(bytes) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses_no_data() { - let buf = &[ - 0x05, 0xAC, // UDP payload size - 0x00, // higher bits - 0x00, 0x00, // EDNS(0) version - 0x00, 0x00, // flags - 0x00, // data length (followed by no data) - ]; - - assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(), - OPT { - udp_payload_size: 1452, - higher_bits: 0, - edns0_version: 0, - flags: 0, - data: vec![], - }); - } - - #[test] - fn parses_with_data() { - let buf = &[ - 0x05, 0xAC, // UDP payload size - 0x00, // higher bits - 0x00, 0x00, // EDNS(0) version - 0x00, 0x00, // flags - 0x04, // data length - 0x01, 0x02, 0x03, 0x04, // data - ]; - - assert_eq!(OPT::read(&mut Cursor::new(buf)).unwrap(), - OPT { - udp_payload_size: 1452, - higher_bits: 0, - edns0_version: 0, - flags: 0, - data: vec![1, 2, 3, 4], - }); - } - - #[test] - fn record_empty() { - assert_eq!(OPT::read(&mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x05, // half a UDP payload size - ]; - - assert_eq!(OPT::read(&mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/others.rs b/dns/src/record/others.rs deleted file mode 100644 index e9b278a..0000000 --- a/dns/src/record/others.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::fmt; - - -/// A number representing a record type dog can’t deal with. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum UnknownQtype { - - /// An rtype number that dog is aware of, but does not know how to parse. - HeardOf(&'static str, u16), - - /// A completely unknown rtype number. - UnheardOf(u16), -} - -impl UnknownQtype { - - /// Searches the list for an unknown type with the given name, returning a - /// `HeardOf` variant if one is found, and `None` otherwise. - pub fn from_type_name(type_name: &str) -> Option { - let (name, num) = TYPES.iter().find(|t| t.0.eq_ignore_ascii_case(type_name))?; - Some(Self::HeardOf(name, *num)) - } - - /// Returns the type number behind this unknown type. - pub fn type_number(self) -> u16 { - match self { - Self::HeardOf(_, num) | - Self::UnheardOf(num) => num, - } - } -} - -impl From for UnknownQtype { - fn from(qtype: u16) -> Self { - match TYPES.iter().find(|t| t.1 == qtype) { - Some(tuple) => Self::HeardOf(tuple.0, qtype), - None => Self::UnheardOf(qtype), - } - } -} - -impl fmt::Display for UnknownQtype { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::HeardOf(name, _) => write!(f, "{}", name), - Self::UnheardOf(num) => write!(f, "{}", num), - } - } -} - - -/// Mapping of record type names to their assigned numbers. -static TYPES: &[(&str, u16)] = &[ - ("AFSDB", 18), - ("ANY", 255), - ("APL", 42), - ("AXFR", 252), - ("CDNSKEY", 60), - ("CDS", 59), - ("CERT", 37), - ("CSYNC", 62), - ("DHCID", 49), - ("DLV", 32769), - ("DNAME", 39), - ("DNSKEEYE", 48), - ("DS", 43), - ("HIP", 55), - ("IPSECKEY", 45), - ("IXFR", 251), - ("KEY", 25), - ("KX", 36), - ("NSEC", 47), - ("NSEC3", 50), - ("NSEC3PARAM", 51), - ("OPENPGPKEY", 61), - ("RRSIG", 46), - ("RP", 17), - ("SIG", 24), - ("SMIMEA", 53), - ("TA", 32768), - ("TKEY", 249), - ("TSIG", 250), - ("URI", 256), -]; - - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn known() { - assert_eq!(UnknownQtype::from(46).to_string(), - String::from("RRSIG")); - } - - #[test] - fn unknown() { - assert_eq!(UnknownQtype::from(4444).to_string(), - String::from("4444")); - } -} diff --git a/dns/src/record/ptr.rs b/dns/src/record/ptr.rs deleted file mode 100644 index f9bda1f..0000000 --- a/dns/src/record/ptr.rs +++ /dev/null @@ -1,91 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **PTR** record, which holds a _pointer_ to a canonical name. This is -/// most often used for reverse DNS lookups. -/// -/// # Encoding -/// -/// The text encoding is not specified, but this crate treats it as UTF-8. -/// Invalid bytes are turned into the replacement character. -/// -/// # References -/// -/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct PTR { - - /// The CNAME contained in the record. - pub cname: Labels, -} - -impl Wire for PTR { - const NAME: &'static str = "PTR"; - const RR_TYPE: u16 = 12; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let (cname, cname_length) = c.read_labels()?; - trace!("Parsed cname -> {:?}", cname); - - if stated_length == cname_length { - trace!("Length is correct"); - Ok(Self { cname }) - } - else { - warn!("Length is incorrect (stated length {:?}, cname length {:?}", stated_length, cname_length); - Err(WireError::WrongLabelLength { stated_length, length_after_labels: cname_length }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x03, 0x64, 0x6e, 0x73, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, // cname - 0x00, // cname terminator - ]; - - assert_eq!(PTR::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - PTR { - cname: Labels::encode("dns.google").unwrap(), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x03, 0x65, 0x66, 0x67, // cname - 0x00, // cname terminator - ]; - - assert_eq!(PTR::read(6, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 6, length_after_labels: 5 })); - } - - #[test] - fn record_empty() { - assert_eq!(PTR::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x03, 0x64, // the start of a cname - ]; - - assert_eq!(PTR::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/soa.rs b/dns/src/record/soa.rs deleted file mode 100644 index d413b0b..0000000 --- a/dns/src/record/soa.rs +++ /dev/null @@ -1,153 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **SOA** _(start of authority)_ record, which contains administrative -/// information about the zone the domain is in. These are returned when a -/// server does not have a record for a domain. -/// -/// # References -/// -/// - [RFC 1035 §3.3.13](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct SOA { - - /// The primary master name for this server. - pub mname: Labels, - - /// The e-mail address of the administrator responsible for this DNS zone. - pub rname: Labels, - - /// A serial number for this DNS zone. - pub serial: u32, - - /// Duration, in seconds, after which secondary nameservers should query - /// the master for _its_ SOA record. - pub refresh_interval: u32, - - /// Duration, in seconds, after which secondary nameservers should retry - /// requesting the serial number from the master if it does not respond. - /// It should be less than `refresh`. - pub retry_interval: u32, - - /// Duration, in seconds, after which secondary nameservers should stop - /// answering requests for this zone if the master does not respond. - /// It should be greater than the sum of `refresh` and `retry`. - pub expire_limit: u32, - - /// Duration, in seconds, of the minimum time-to-live. - pub minimum_ttl: u32, -} - -impl Wire for SOA { - const NAME: &'static str = "SOA"; - const RR_TYPE: u16 = 6; - - #[allow(clippy::similar_names)] - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let (mname, mname_length) = c.read_labels()?; - trace!("Parsed mname -> {:?}", mname); - - let (rname, rname_length) = c.read_labels()?; - trace!("Parsed rname -> {:?}", rname); - - let serial = c.read_u32::()?; - trace!("Parsed serial -> {:?}", serial); - - let refresh_interval = c.read_u32::()?; - trace!("Parsed refresh interval -> {:?}", refresh_interval); - - let retry_interval = c.read_u32::()?; - trace!("Parsed retry interval -> {:?}", retry_interval); - - let expire_limit = c.read_u32::()?; - trace!("Parsed expire limit -> {:?}", expire_limit); - - let minimum_ttl = c.read_u32::()?; - trace!("Parsed minimum TTL -> {:?}", minimum_ttl); - - let length_after_labels = 4 * 5 + mname_length + rname_length; - if stated_length == length_after_labels { - trace!("Length is correct"); - Ok(Self { - mname, rname, serial, refresh_interval, - retry_interval, expire_limit, minimum_ttl, - }) - } - else { - warn!("Length is incorrect (stated length {:?}, mname plus rname plus fields length {:?})", stated_length, length_after_labels); - Err(WireError::WrongLabelLength { stated_length, length_after_labels }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // mname - 0x00, // mname terminator - 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, // rname - 0x00, // rname terminator - 0x5d, 0x3c, 0xef, 0x02, // Serial - 0x00, 0x01, 0x51, 0x80, // Refresh interval - 0x00, 0x00, 0x1c, 0x20, // Retry interval - 0x00, 0x09, 0x3a, 0x80, // Expire limit - 0x00, 0x00, 0x01, 0x2c, // Minimum TTL - ]; - - assert_eq!(SOA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - SOA { - mname: Labels::encode("bsago.me").unwrap(), - rname: Labels::encode("bsago.me").unwrap(), - serial: 1564274434, - refresh_interval: 86400, - retry_interval: 7200, - expire_limit: 604800, - minimum_ttl: 300, - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x03, 0x65, 0x66, 0x67, // mname - 0x00, // mname terminator - 0x03, 0x65, 0x66, 0x67, // rname - 0x00, // rname terminator - 0x5d, 0x3c, 0xef, 0x02, // Serial - 0x00, 0x01, 0x51, 0x80, // Refresh interval - 0x00, 0x00, 0x1c, 0x20, // Retry interval - 0x00, 0x09, 0x3a, 0x80, // Expire limit - 0x00, 0x00, 0x01, 0x2c, // Minimum TTL - ]; - - assert_eq!(SOA::read(89, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 89, length_after_labels: 30 })); - } - - #[test] - fn record_empty() { - assert_eq!(SOA::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x05, 0x62, // the start of an mname - ]; - - assert_eq!(SOA::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/srv.rs b/dns/src/record/srv.rs deleted file mode 100644 index fdbce71..0000000 --- a/dns/src/record/srv.rs +++ /dev/null @@ -1,118 +0,0 @@ -use log::*; - -use crate::strings::{Labels, ReadLabels}; -use crate::wire::*; - - -/// A **SRV** record, which contains an IP address as well as a port number, -/// for specifying the location of services more precisely. -/// -/// # References -/// -/// - [RFC 2782](https://tools.ietf.org/html/rfc2782) — A DNS RR for -/// specifying the location of services (February 2000) -#[derive(PartialEq, Debug)] -pub struct SRV { - - /// The priority of this host among all that get returned. Lower values - /// are higher priority. - pub priority: u16, - - /// A weight to choose among results with the same priority. Higher values - /// are higher priority. - pub weight: u16, - - /// The port the service is serving on. - pub port: u16, - - /// The hostname of the machine the service is running on. - pub target: Labels, -} - -impl Wire for SRV { - const NAME: &'static str = "SRV"; - const RR_TYPE: u16 = 33; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let priority = c.read_u16::()?; - trace!("Parsed priority -> {:?}", priority); - - let weight = c.read_u16::()?; - trace!("Parsed weight -> {:?}", weight); - - let port = c.read_u16::()?; - trace!("Parsed port -> {:?}", port); - - let (target, target_length) = c.read_labels()?; - trace!("Parsed target -> {:?}", target); - - let length_after_labels = 3 * 2 + target_length; - if stated_length == length_after_labels { - trace!("Length is correct"); - Ok(Self { priority, weight, port, target }) - } - else { - warn!("Length is incorrect (stated length {:?}, fields plus target length {:?})", stated_length, length_after_labels); - Err(WireError::WrongLabelLength { stated_length, length_after_labels }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x01, // priority - 0x00, 0x01, // weight - 0x92, 0x7c, // port - 0x03, 0x61, 0x74, 0x61, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x04, - 0x6e, 0x6f, 0x64, 0x65, 0x03, 0x64, 0x63, 0x31, 0x06, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, // target - 0x00, // target terminator - ]; - - assert_eq!(SRV::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - SRV { - priority: 1, - weight: 1, - port: 37500, - target: Labels::encode("ata.local.node.dc1.consul").unwrap(), - }); - } - - #[test] - fn incorrect_record_length() { - let buf = &[ - 0x00, 0x01, // priority - 0x00, 0x01, // weight - 0x92, 0x7c, // port - 0x03, 0x61, 0x74, 0x61, // target - 0x00, // target terminator - ]; - - assert_eq!(SRV::read(16, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 16, length_after_labels: 11 })); - } - - #[test] - fn record_empty() { - assert_eq!(SRV::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, // half a priority - ]; - - assert_eq!(SRV::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/sshfp.rs b/dns/src/record/sshfp.rs deleted file mode 100644 index 2d5ae9a..0000000 --- a/dns/src/record/sshfp.rs +++ /dev/null @@ -1,140 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **SSHFP** _(secure shell fingerprint)_ record, which contains the -/// fingerprint of an SSH public key. -/// -/// # References -/// -/// - [RFC 4255](https://tools.ietf.org/html/rfc4255) — Using DNS to Securely -/// Publish Secure Shell (SSH) Key Fingerprints (January 2006) -#[derive(PartialEq, Debug)] -pub struct SSHFP { - - /// The algorithm of the public key. This is a number with several defined - /// mappings. - pub algorithm: u8, - - /// The type of the fingerprint, which specifies the hashing algorithm - /// used to derive the fingerprint. This is a number with several defined - /// mappings. - pub fingerprint_type: u8, - - /// The fingerprint of the public key. - pub fingerprint: Vec, -} - -impl Wire for SSHFP { - const NAME: &'static str = "SSHFP"; - const RR_TYPE: u16 = 44; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let algorithm = c.read_u8()?; - trace!("Parsed algorithm -> {:?}", algorithm); - - let fingerprint_type = c.read_u8()?; - trace!("Parsed fingerprint type -> {:?}", fingerprint_type); - - if stated_length <= 2 { - let mandated_length = MandatedLength::AtLeast(3); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let fingerprint_length = stated_length - 1 - 1; - let mut fingerprint = vec![0_u8; usize::from(fingerprint_length)]; - c.read_exact(&mut fingerprint)?; - trace!("Parsed fingerprint -> {:#x?}", fingerprint); - - Ok(Self { algorithm, fingerprint_type, fingerprint }) - } -} - -impl SSHFP { - - /// Returns the hexadecimal representation of the fingerprint. - pub fn hex_fingerprint(&self) -> String { - self.fingerprint.iter() - .map(|byte| format!("{:02x}", byte)) - .collect() - } -} - - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parses() { - let buf = &[ - 0x01, // algorithm - 0x01, // fingerprint type - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, // a short fingerprint - ]; - - assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - SSHFP { - algorithm: 1, - fingerprint_type: 1, - fingerprint: vec![ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26 ], - }); - } - - #[test] - fn one_byte_fingerprint() { - let buf = &[ - 0x01, // algorithm - 0x01, // fingerprint type - 0x21, // an extremely short fingerprint - ]; - - assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - SSHFP { - algorithm: 1, - fingerprint_type: 1, - fingerprint: vec![ 0x21 ], - }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x01, // algorithm - 0x01, // fingerprint type - ]; - - assert_eq!(SSHFP::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 2, mandated_length: MandatedLength::AtLeast(3) })); - } - - #[test] - fn record_empty() { - assert_eq!(SSHFP::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x01, // algorithm - ]; - - assert_eq!(SSHFP::read(6, &mut Cursor::new(buf)), - Err(WireError::IO)); - } - - #[test] - fn hex_rep() { - let sshfp = SSHFP { - algorithm: 1, - fingerprint_type: 1, - fingerprint: vec![ 0xf3, 0x48, 0xcd, 0xc9 ], - }; - - assert_eq!(sshfp.hex_fingerprint(), - String::from("f348cdc9")); - } -} diff --git a/dns/src/record/tlsa.rs b/dns/src/record/tlsa.rs deleted file mode 100644 index 2d0625e..0000000 --- a/dns/src/record/tlsa.rs +++ /dev/null @@ -1,142 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **TLSA** _(TLS authentication)_ record, which contains a TLS certificate -/// (or a public key, or its hash), associating it with a domain. -/// -/// # References -/// -/// - [RFC 6698](https://tools.ietf.org/html/rfc6698) — The DNS-Based -/// Authentication of Named Entities (DANE) Transport Layer Security -/// Protocol: TLSA (August 2012) -#[derive(PartialEq, Debug)] -pub struct TLSA { - - /// A number representing the purpose of the certificate. - pub certificate_usage: u8, - - /// A number representing which part of the certificate is returned in the - /// data. This could be the full certificate, or just the public key. - pub selector: u8, - - /// A number representing whether a certificate should be associated with - /// the exact data, or with a hash of it. - pub matching_type: u8, - - /// A series of bytes representing the certificate. - pub certificate_data: Vec, -} - - -impl Wire for TLSA { - const NAME: &'static str = "TLSA"; - const RR_TYPE: u16 = 52; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - - let certificate_usage = c.read_u8()?; - trace!("Parsed certificate_usage -> {:?}", certificate_usage); - - let selector = c.read_u8()?; - trace!("Parsed selector -> {:?}", selector); - - let matching_type = c.read_u8()?; - trace!("Parsed matching type -> {:?}", matching_type); - - if stated_length <= 3 { - let mandated_length = MandatedLength::AtLeast(4); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let certificate_data_length = stated_length - 1 - 1 - 1; - let mut certificate_data = vec![0_u8; usize::from(certificate_data_length)]; - c.read_exact(&mut certificate_data)?; - trace!("Parsed fingerprint -> {:#x?}", certificate_data); - - Ok(Self { certificate_usage, selector, matching_type, certificate_data }) - } -} - -impl TLSA { - - /// Returns the hexadecimal representation of the fingerprint. - pub fn hex_certificate_data(&self) -> String { - self.certificate_data.iter() - .map(|byte| format!("{:02x}", byte)) - .collect() - } -} - - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parses() { - let buf = &[ - 0x03, // certificate usage - 0x01, // selector - 0x01, // matching type - 0x05, 0x95, 0x98, 0x11, 0x22, 0x33 // data - ]; - - assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TLSA { - certificate_usage: 3, - selector: 1, - matching_type: 1, - certificate_data: vec![ 0x05, 0x95, 0x98, 0x11, 0x22, 0x33 ], - }); - } - - #[test] - fn one_byte_certificate() { - let buf = &[ - 0x03, // certificate usage - 0x01, // selector - 0x01, // matching type - 0x05, // one byte of data - ]; - - assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TLSA { - certificate_usage: 3, - selector: 1, - matching_type: 1, - certificate_data: vec![ 0x05 ], - }); - } - - #[test] - fn record_too_short() { - let buf = &[ - 0x03, // certificate usage - 0x01, // selector - 0x01, // matching type - ]; - - assert_eq!(TLSA::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 3, mandated_length: MandatedLength::AtLeast(4) })); - } - - #[test] - fn record_empty() { - assert_eq!(TLSA::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x01, // certificate_usage - ]; - - assert_eq!(TLSA::read(6, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} - diff --git a/dns/src/record/txt.rs b/dns/src/record/txt.rs deleted file mode 100644 index f5ac08e..0000000 --- a/dns/src/record/txt.rs +++ /dev/null @@ -1,231 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **TXT** record, which holds arbitrary descriptive text. -/// -/// # Encoding -/// -/// The text encoding is not specified, but this crate treats it as UTF-8. -/// Invalid bytes are turned into the replacement character. -/// -/// # References -/// -/// - [RFC 1035 §3.3.14](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -#[derive(PartialEq, Debug)] -pub struct TXT { - - /// The messages contained in the record. - pub messages: Vec>, -} - -impl Wire for TXT { - const NAME: &'static str = "TXT"; - const RR_TYPE: u16 = 16; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let mut messages = Vec::new(); - let mut total_length = 0_u16; - - loop { - let mut buf = Vec::new(); - - loop { - let next_length = c.read_u8()?; - total_length += u16::from(next_length) + 1; - trace!("Parsed slice length -> {:?} (total so far {:?})", next_length, total_length); - - for _ in 0 .. next_length { - buf.push(c.read_u8()?); - } - - if next_length < 255 { - break; - } - else { - trace!("Got length 255, so looping"); - } - } - - let message = buf.into_boxed_slice(); - trace!("Parsed message -> {:?}", String::from_utf8_lossy(&message)); - messages.push(message); - - if total_length >= stated_length { - break; - } - } - - if stated_length == total_length { - trace!("Length is correct"); - Ok(Self { messages }) - } - else { - warn!("Length is incorrect (stated length {:?}, messages length {:?})", stated_length, total_length); - Err(WireError::WrongLabelLength { stated_length, length_after_labels: total_length }) - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses_one_iteration() { - let buf = &[ - 0x06, // message chunk length - 0x74, 0x78, 0x74, 0x20, 0x6d, 0x65, // message chunk - ]; - - assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TXT { - messages: vec![ Box::new(*b"txt me") ], - }); - } - - #[test] - fn parses_two_iterations() { - let buf = &[ - 0xFF, // message chunk length - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, // exactly two hundred and fifty five ‘A’s (screaming) - 0x04, // message chunk length - 0x41, 0x41, 0x41, 0x41, // four more ‘A’s (the scream abruptly stops) - ]; - - assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TXT { - messages: vec![ - Box::new(*b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ - AAAAAAAAAAAAAAAAAAAAAAAAAAA"), - ], - }); - // did you know you can just _write_ code like this, and nobody will stop you? - } - - #[test] - fn right_at_the_limit() { - let buf = &[ - 0xFE, // message chunk length - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, // exactly two hundred and fifty four ‘B’s (a hive) - ]; - - assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TXT { - messages: vec![ - Box::new(*b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ - BBBBBBBBBBBBBBBBBBBBBB"), - ], - }); - } - - #[test] - fn another_message() { - let buf = &[ - 0x06, // message chunk length - 0x74, 0x78, 0x74, 0x20, 0x6d, 0x65, // message chunk - 0x06, // message chunk length - 0x79, 0x61, 0x20, 0x62, 0x65, 0x62, // message chunk - ]; - - assert_eq!(TXT::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - TXT { - messages: vec![ - Box::new(*b"txt me"), - Box::new(*b"ya beb"), - ], - }); - } - - #[test] - fn length_too_short() { - let buf = &[ - 0x06, // message chunk length - 0x74, 0x78, 0x74, 0x20, 0x6d, 0x65, // message chunk - ]; - - assert_eq!(TXT::read(2, &mut Cursor::new(buf)), - Err(WireError::WrongLabelLength { stated_length: 2, length_after_labels: 7 })); - } - - #[test] - fn record_empty() { - assert_eq!(TXT::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x06, 0x74, // the start of a message - ]; - - assert_eq!(TXT::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/record/uri.rs b/dns/src/record/uri.rs deleted file mode 100644 index 9a01270..0000000 --- a/dns/src/record/uri.rs +++ /dev/null @@ -1,123 +0,0 @@ -use log::*; - -use crate::wire::*; - - -/// A **URI** record, which holds a URI along with weight and priority values -/// to balance between several records. -/// -/// # References -/// -/// - [RFC 7553](https://tools.ietf.org/html/rfc7553) — The Uniform Resource -/// Identifier (URI) DNS Resource Record (June 2015) -/// - [RFC 3986](https://tools.ietf.org/html/rfc3986) — Uniform Resource -/// Identifier (URI): Generic Syntax (January 2005) -#[derive(PartialEq, Debug)] -pub struct URI { - - /// The priority of the URI. Clients are supposed to contact the URI with - /// the lowest priority out of all the ones it can reach. - pub priority: u16, - - /// The weight of the URI, which specifies a relative weight for entries - /// with the same priority. - pub weight: u16, - - /// The URI contained in the record. Since all we are doing is displaying - /// it to the user, we do not need to parse it for accuracy. - pub target: Box<[u8]>, -} - -impl Wire for URI { - const NAME: &'static str = "URI"; - const RR_TYPE: u16 = 256; - - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result { - let priority = c.read_u16::()?; - trace!("Parsed priority -> {:?}", priority); - - let weight = c.read_u16::()?; - trace!("Parsed weight -> {:?}", weight); - - // The target must not be empty. - if stated_length <= 4 { - let mandated_length = MandatedLength::AtLeast(5); - return Err(WireError::WrongRecordLength { stated_length, mandated_length }); - } - - let remaining_length = stated_length - 4; - let mut target = vec![0_u8; usize::from(remaining_length)].into_boxed_slice(); - c.read_exact(&mut target)?; - trace!("Parsed target -> {:?}", String::from_utf8_lossy(&target)); - - Ok(Self { priority, weight, target }) - } -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn parses() { - let buf = &[ - 0x00, 0x0A, // priority - 0x00, 0x10, // weight - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x72, 0x66, 0x63, - 0x73, 0x2e, 0x69, 0x6f, 0x2f, // uri - ]; - - assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - URI { - priority: 10, - weight: 16, - target: Box::new(*b"https://rfcs.io/"), - }); - } - - #[test] - fn one_byte_of_uri() { - let buf = &[ - 0x00, 0x0A, // priority - 0x00, 0x10, // weight - 0x2f, // one byte of uri (invalid but still a legitimate DNS record) - ]; - - assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)).unwrap(), - URI { - priority: 10, - weight: 16, - target: Box::new(*b"/"), - }); - } - - #[test] - fn missing_any_data() { - let buf = &[ - 0x00, 0x0A, // priority - 0x00, 0x10, // weight - ]; - - assert_eq!(URI::read(buf.len() as _, &mut Cursor::new(buf)), - Err(WireError::WrongRecordLength { stated_length: 4, mandated_length: MandatedLength::AtLeast(5) })); - } - - #[test] - fn record_empty() { - assert_eq!(URI::read(0, &mut Cursor::new(&[])), - Err(WireError::IO)); - } - - #[test] - fn buffer_ends_abruptly() { - let buf = &[ - 0x00, 0x0A, // half a priority - ]; - - assert_eq!(URI::read(23, &mut Cursor::new(buf)), - Err(WireError::IO)); - } -} diff --git a/dns/src/strings.rs b/dns/src/strings.rs deleted file mode 100644 index 46489e2..0000000 --- a/dns/src/strings.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Reading strings from the DNS wire protocol. - -use std::convert::TryFrom; -use std::fmt; -use std::io::{self, Write}; - -use byteorder::{ReadBytesExt, WriteBytesExt}; -use log::*; - -use crate::wire::*; - - -/// Domain names in the DNS protocol are encoded as **Labels**, which are -/// segments of ASCII characters prefixed by their length. When written out, -/// each segment is followed by a dot. -/// -/// The maximum length of a segment is 255 characters. -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] -pub struct Labels { - segments: Vec<(u8, String)>, -} - -#[cfg(feature = "with_idna")] -fn label_to_ascii(label: &str) -> Result { - let flags = unic_idna::Flags{use_std3_ascii_rules: false, transitional_processing: false, verify_dns_length: true}; - unic_idna::to_ascii(label, flags) -} - -#[cfg(not(feature = "with_idna"))] -fn label_to_ascii(label: &str) -> Result { - Ok(label.to_owned()) -} - -impl Labels { - - /// Creates a new empty set of labels, which represent the root of the DNS - /// as a domain with no name. - pub fn root() -> Self { - Self { segments: Vec::new() } - } - - /// Encodes the given input string as labels. If any segment is too long, - /// returns that segment as an error. - pub fn encode(input: &str) -> Result { - let mut segments = Vec::new(); - - for label in input.split('.') { - if label.is_empty() { - continue; - } - - let label_idn = label_to_ascii(label) - .map_err(|e| { - warn!("Could not encode label {:?}: {:?}", label, e); - label - })?; - - match u8::try_from(label_idn.len()) { - Ok(length) => { - segments.push((length, label_idn)); - } - Err(e) => { - warn!("Could not encode label {:?}: {}", label, e); - return Err(label); - } - } - } - - Ok(Self { segments }) - } - - /// Returns the number of segments. - pub fn len(&self) -> usize { - self.segments.len() - } - - /// Returns a new set of labels concatenating two names. - pub fn extend(&self, other: &Self) -> Self { - let mut segments = self.segments.clone(); - segments.extend_from_slice(&other.segments); - Self { segments } - } -} - -impl fmt::Display for Labels { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for (_, segment) in &self.segments { - write!(f, "{}.", segment)?; - } - - Ok(()) - } -} - -/// An extension for `Cursor` that enables reading compressed domain names -/// from DNS packets. -pub(crate) trait ReadLabels { - - /// Read and expand a compressed domain name. - fn read_labels(&mut self) -> Result<(Labels, u16), WireError>; -} - -impl ReadLabels for Cursor<&[u8]> { - fn read_labels(&mut self) -> Result<(Labels, u16), WireError> { - let mut labels = Labels { segments: Vec::new() }; - let bytes_read = read_string_recursive(&mut labels, self, &mut Vec::new())?; - Ok((labels, bytes_read)) - } -} - - -/// An extension for `Write` that enables writing domain names. -pub(crate) trait WriteLabels { - - /// Write a domain name. - /// - /// The names being queried are written with one byte slice per - /// domain segment, preceded by each segment’s length, with the - /// whole thing ending with a segment of zero length. - /// - /// So “dns.lookup.dog” would be encoded as: - /// “3, dns, 6, lookup, 3, dog, 0”. - fn write_labels(&mut self, input: &Labels) -> io::Result<()>; -} - -impl WriteLabels for W { - fn write_labels(&mut self, input: &Labels) -> io::Result<()> { - for (length, label) in &input.segments { - self.write_u8(*length)?; - - for b in label.as_bytes() { - self.write_u8(*b)?; - } - } - - self.write_u8(0)?; // terminate the string - Ok(()) - } -} - - -const RECURSION_LIMIT: usize = 8; - -/// Reads bytes from the given cursor into the given buffer, using the list of -/// recursions to track backtracking positions. Returns the count of bytes -/// that had to be read to produce the string, including the bytes to signify -/// backtracking, but not including the bytes read _during_ backtracking. -#[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] -fn read_string_recursive(labels: &mut Labels, c: &mut Cursor<&[u8]>, recursions: &mut Vec) -> Result { - let mut bytes_read = 0; - - loop { - let byte = c.read_u8()?; - bytes_read += 1; - - if byte == 0 { - break; - } - - else if byte >= 0b_1100_0000 { - let name_one = byte - 0b1100_0000; - let name_two = c.read_u8()?; - bytes_read += 1; - let offset = u16::from_be_bytes([name_one, name_two]); - - if recursions.contains(&offset) { - warn!("Hit previous offset ({}) decoding string", offset); - return Err(WireError::TooMuchRecursion(recursions.clone().into_boxed_slice())); - } - - recursions.push(offset); - - if recursions.len() >= RECURSION_LIMIT { - warn!("Hit recursion limit ({}) decoding string", RECURSION_LIMIT); - return Err(WireError::TooMuchRecursion(recursions.clone().into_boxed_slice())); - } - - trace!("Backtracking to offset {}", offset); - let new_pos = c.position(); - c.set_position(u64::from(offset)); - - read_string_recursive(labels, c, recursions)?; - - trace!("Coming back to {}", new_pos); - c.set_position(new_pos); - break; - } - - // Otherwise, treat the byte as the length of a label, and read that - // many characters. - else { - let mut name_buf = Vec::new(); - - for _ in 0 .. byte { - let c = c.read_u8()?; - bytes_read += 1; - name_buf.push(c); - } - - let string = String::from_utf8_lossy(&*name_buf).to_string(); - labels.segments.push((byte, string)); - } - } - - Ok(bytes_read) -} - - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - - // The buffers used in these tests contain nothing but the labels we’re - // decoding. In DNS packets found in the wild, the cursor will be able to - // reach all the bytes of the packet, so the Answer section can reference - // strings in the Query section. - - #[test] - fn nothing() { - let buf: &[u8] = &[ - 0x00, // end reading - ]; - - assert_eq!(Cursor::new(buf).read_labels(), - Ok((Labels::root(), 1))); - } - - #[test] - fn one_label() { - let buf: &[u8] = &[ - 0x03, // label of length 3 - b'o', b'n', b'e', // label - 0x00, // end reading - ]; - - assert_eq!(Cursor::new(buf).read_labels(), - Ok((Labels::encode("one.").unwrap(), 5))); - } - - #[test] - fn two_labels() { - let buf: &[u8] = &[ - 0x03, // label of length 3 - b'o', b'n', b'e', // label - 0x03, // label of length 3 - b't', b'w', b'o', // label - 0x00, // end reading - ]; - - assert_eq!(Cursor::new(buf).read_labels(), - Ok((Labels::encode("one.two.").unwrap(), 9))); - } - - #[test] - fn label_followed_by_backtrack() { - let buf: &[u8] = &[ - 0x03, // label of length 3 - b'o', b'n', b'e', // label - 0xc0, 0x06, // skip to position 6 (the next byte) - - 0x03, // label of length 3 - b't', b'w', b'o', // label - 0x00, // end reading - ]; - - assert_eq!(Cursor::new(buf).read_labels(), - Ok((Labels::encode("one.two.").unwrap(), 6))); - } - - #[test] - fn extremely_long_label() { - let mut buf: Vec = vec![ - 0xbf, // label of length 191 - ]; - - buf.extend(vec![0x65; 191]); // the rest of the label - buf.push(0x00); // end reading - - assert_eq!(Cursor::new(&*buf).read_labels().unwrap().1, 193); - } - - #[test] - fn immediate_recursion() { - let buf: &[u8] = &[ - 0xc0, 0x00, // skip to position 0 - ]; - - assert_eq!(Cursor::new(buf).read_labels(), - Err(WireError::TooMuchRecursion(Box::new([ 0 ])))); - } - - #[test] - fn mutual_recursion() { - let buf: &[u8] = &[ - 0xc0, 0x02, // skip to position 2 - 0xc0, 0x00, // skip to position 0 - ]; - - let mut cursor = Cursor::new(buf); - - assert_eq!(cursor.read_labels(), - Err(WireError::TooMuchRecursion(Box::new([ 2, 0 ])))); - } - - #[test] - fn too_much_recursion() { - let buf: &[u8] = &[ - 0xc0, 0x02, // skip to position 2 - 0xc0, 0x04, // skip to position 4 - 0xc0, 0x06, // skip to position 6 - 0xc0, 0x08, // skip to position 8 - 0xc0, 0x0A, // skip to position 10 - 0xc0, 0x0C, // skip to position 12 - 0xc0, 0x0E, // skip to position 14 - 0xc0, 0x10, // skip to position 16 - 0x00, // no label - ]; - - let mut cursor = Cursor::new(buf); - - assert_eq!(cursor.read_labels(), - Err(WireError::TooMuchRecursion(Box::new([ 2, 4, 6, 8, 10, 12, 14, 16 ])))); - } -} diff --git a/dns/src/types.rs b/dns/src/types.rs deleted file mode 100644 index 12d5c6c..0000000 --- a/dns/src/types.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! DNS packets are traditionally implemented with both the request and -//! response packets at the same type. After all, both follow the same format, -//! with the request packet having zero answer fields, and the response packet -//! having at least one record in its answer fields. - -use crate::record::{Record, RecordType, OPT}; -use crate::strings::Labels; - - -/// A request that gets sent out over a transport. -#[derive(PartialEq, Debug)] -pub struct Request { - - /// The transaction ID of this request. This is used to make sure - /// different DNS packets don’t answer each other’s questions. - pub transaction_id: u16, - - /// The flags that accompany every DNS packet. - pub flags: Flags, - - /// The query that this request is making. Only one query is allowed per - /// request, as traditionally, DNS servers only respond to the first query - /// in a packet. - pub query: Query, - - /// An additional record that may be sent as part of the query. - pub additional: Option, -} - - -/// A response obtained from a DNS server. -#[derive(PartialEq, Debug)] -pub struct Response { - - /// The transaction ID, which should match the ID of the request. - pub transaction_id: u16, - - /// The flags that accompany every DNS packet. - pub flags: Flags, - - /// The queries section. - pub queries: Vec, - - /// The answers section. - pub answers: Vec, - - /// The authoritative nameservers section. - pub authorities: Vec, - - /// The additional records section. - pub additionals: Vec, -} - - -/// A DNS query section. -#[derive(PartialEq, Debug)] -pub struct Query { - - /// The domain name being queried, in human-readable dotted notation. - pub qname: Labels, - - /// The class number. - pub qclass: QClass, - - /// The type number. - pub qtype: RecordType, -} - - -/// A DNS answer section. -#[derive(PartialEq, Debug)] -pub enum Answer { - - /// This is a standard answer with every field. - Standard { - - /// The domain name being answered for. - qname: Labels, - - /// This answer’s class. - qclass: QClass, - - /// The time-to-live duration, in seconds. - ttl: u32, - - /// The record contained in this answer. - record: Record, - }, - - /// This is a pseudo-record answer, so some of the fields (class and TTL) - /// have different meaning. - Pseudo { - - /// The domain name being answered for. - qname: Labels, - - /// The OPT record contained in this answer. - opt: OPT, - }, -} - - -/// A DNS record class. Of these, the only one that’s in regular use anymore -/// is the Internet class. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum QClass { - - /// The **Internet** class. - IN, - - /// The **Chaosnet** class. - CH, - - /// The **Hesiod** class. - HS, - - /// A class number that does not map to any known class. - Other(u16), -} - - -/// The flags that accompany every DNS packet. -#[derive(PartialEq, Debug, Copy, Clone)] -pub struct Flags { - - /// Whether this packet is a response packet. - pub response: bool, - - /// The operation being performed. - pub opcode: Opcode, - - /// In a response, whether the server is providing authoritative DNS responses. - pub authoritative: bool, - - /// In a response, whether this message has been truncated by the transport. - pub truncated: bool, - - /// In a query, whether the server may query other nameservers recursively. - /// It is up to the server whether it will actually do this. - pub recursion_desired: bool, - - /// In a response, whether the server allows recursive query support. - pub recursion_available: bool, - - /// In a response, whether the server is marking this data as authentic. - pub authentic_data: bool, - - /// In a request, whether the server should disable its authenticity - /// checking for the request’s queries. - pub checking_disabled: bool, - - /// In a response, a code indicating an error if one occurred. - pub error_code: Option, -} - - -/// A number representing the operation being performed. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum Opcode { - - /// This request is a standard query, or this response is answering a - /// standard query. - Query, - - /// Any other opcode. This can be from 1 to 15, as the opcode field is - /// four bits wide, and 0 is taken. - Other(u8), -} - - -/// A code indicating an error. -/// -/// # References -/// -/// - [RFC 6895 §2.3](https://tools.ietf.org/html/rfc6895#section-2.3) — Domain -/// Name System (DNS) IANA Considerations (April 2013) -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum ErrorCode { - - /// `FormErr` — The server was unable to interpret the query. - FormatError, - - /// `ServFail` — There was a problem with the server. - ServerFailure, - - /// `NXDomain` — The domain name referenced in the query does not exist. - NXDomain, - - /// `NotImp` — The server does not support one of the requested features. - NotImplemented, - - /// `Refused` — The server was able to interpret the query, but refused to - /// fulfil it. - QueryRefused, - - /// `BADVERS` and `BADSIG` — The server did not accept the EDNS version, - /// or failed to verify a signature. The same code is used for both. - BadVersion, - - /// An error code with no currently-defined meaning. - Other(u16), - - /// An error code within the ‘Reserved for Private Use’ range. - Private(u16) -} - - -impl Answer { - - /// Whether this Answer holds a standard record, not a pseudo record. - pub fn is_standard(&self) -> bool { - matches!(self, Self::Standard { .. }) - } -} diff --git a/dns/src/wire.rs b/dns/src/wire.rs deleted file mode 100644 index 2559ab3..0000000 --- a/dns/src/wire.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! Parsing the DNS wire protocol. - -pub(crate) use std::io::{Cursor, Read}; -pub(crate) use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; - -use std::io; -use log::*; - -use crate::record::{Record, RecordType, OPT}; -use crate::strings::{Labels, ReadLabels, WriteLabels}; -use crate::types::*; - - -impl Request { - - /// Converts this request to a vector of bytes. - pub fn to_bytes(&self) -> io::Result> { - let mut bytes = Vec::with_capacity(32); - - bytes.write_u16::(self.transaction_id)?; - bytes.write_u16::(self.flags.to_u16())?; - - bytes.write_u16::(1)?; // query count - bytes.write_u16::(0)?; // answer count - bytes.write_u16::(0)?; // authority RR count - bytes.write_u16::(if self.additional.is_some() { 1 } else { 0 })?; // additional RR count - - bytes.write_labels(&self.query.qname)?; - bytes.write_u16::(self.query.qtype.type_number())?; - bytes.write_u16::(self.query.qclass.to_u16())?; - - if let Some(opt) = &self.additional { - bytes.write_u8(0)?; // usually a name - bytes.write_u16::(OPT::RR_TYPE)?; - bytes.extend(opt.to_bytes()?); - } - - Ok(bytes) - } - - /// Returns the OPT record to be sent as part of requests. - pub fn additional_record() -> OPT { - OPT { - udp_payload_size: 512, - higher_bits: 0, - edns0_version: 0, - flags: 0, - data: Vec::new(), - } - } -} - - -impl Response { - - /// Reads bytes off of the given slice, parsing them into a response. - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - pub fn from_bytes(bytes: &[u8]) -> Result { - info!("Parsing response"); - trace!("Bytes -> {:?}", bytes); - let mut c = Cursor::new(bytes); - - let transaction_id = c.read_u16::()?; - trace!("Read txid -> {:?}", transaction_id); - - let flags = Flags::from_u16(c.read_u16::()?); - trace!("Read flags -> {:#?}", flags); - - let query_count = c.read_u16::()?; - let answer_count = c.read_u16::()?; - let authority_count = c.read_u16::()?; - let additional_count = c.read_u16::()?; - - // We can pre-allocate these vectors by giving them an initial - // capacity based on the count fields. But because the count fields - // are user-controlled (with a maximum of 2^16 - 1) we cannot trust - // them _entirely_, so cap the pre-allocation if the count looks - // arbitrarily large (9 seems about right). - - let mut queries = Vec::with_capacity(usize::from(query_count.min(9))); - debug!("Reading {}x query from response", query_count); - for _ in 0 .. query_count { - let (qname, _) = c.read_labels()?; - queries.push(Query::from_bytes(qname, &mut c)?); - } - - let mut answers = Vec::with_capacity(usize::from(answer_count.min(9))); - debug!("Reading {}x answer from response", answer_count); - for _ in 0 .. answer_count { - let (qname, _) = c.read_labels()?; - answers.push(Answer::from_bytes(qname, &mut c)?); - } - - let mut authorities = Vec::with_capacity(usize::from(authority_count.min(9))); - debug!("Reading {}x authority from response", authority_count); - for _ in 0 .. authority_count { - let (qname, _) = c.read_labels()?; - authorities.push(Answer::from_bytes(qname, &mut c)?); - } - - let mut additionals = Vec::with_capacity(usize::from(additional_count.min(9))); - debug!("Reading {}x additional answer from response", additional_count); - for _ in 0 .. additional_count { - let (qname, _) = c.read_labels()?; - additionals.push(Answer::from_bytes(qname, &mut c)?); - } - - Ok(Self { transaction_id, flags, queries, answers, authorities, additionals }) - } -} - - -impl Query { - - /// Reads bytes from the given cursor, and parses them into a query with - /// the given domain name. - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result { - let qtype_number = c.read_u16::()?; - trace!("Read qtype number -> {:?}", qtype_number ); - - let qtype = RecordType::from(qtype_number); - trace!("Found qtype -> {:?}", qtype ); - - let qclass = QClass::from_u16(c.read_u16::()?); - trace!("Read qclass -> {:?}", qtype); - - Ok(Self { qtype, qclass, qname }) - } -} - - -impl Answer { - - /// Reads bytes from the given cursor, and parses them into an answer with - /// the given domain name. - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result { - let qtype_number = c.read_u16::()?; - trace!("Read qtype number -> {:?}", qtype_number ); - - if qtype_number == OPT::RR_TYPE { - let opt = OPT::read(c)?; - Ok(Self::Pseudo { qname, opt }) - } - else { - let qtype = RecordType::from(qtype_number); - trace!("Found qtype -> {:?}", qtype ); - - let qclass = QClass::from_u16(c.read_u16::()?); - trace!("Read qclass -> {:?}", qtype); - - let ttl = c.read_u32::()?; - trace!("Read TTL -> {:?}", ttl); - - let record_length = c.read_u16::()?; - trace!("Read record length -> {:?}", record_length); - - let record = Record::from_bytes(qtype, record_length, c)?; - Ok(Self::Standard { qclass, qname, record, ttl }) - } - } -} - - -impl Record { - - /// Reads at most `len` bytes from the given curser, and parses them into - /// a record structure depending on the type number, which has already been read. - #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)] - fn from_bytes(record_type: RecordType, len: u16, c: &mut Cursor<&[u8]>) -> Result { - if cfg!(feature = "with_mutagen") { - warn!("Mutation is enabled!"); - } - - macro_rules! read_record { - ($record:tt) => { { - info!("Parsing {} record (type {}, len {})", crate::record::$record::NAME, record_type.type_number(), len); - Wire::read(len, c).map(Self::$record) - } } - } - - match record_type { - RecordType::A => read_record!(A), - RecordType::AAAA => read_record!(AAAA), - RecordType::CAA => read_record!(CAA), - RecordType::CNAME => read_record!(CNAME), - RecordType::EUI48 => read_record!(EUI48), - RecordType::EUI64 => read_record!(EUI64), - RecordType::HINFO => read_record!(HINFO), - RecordType::LOC => read_record!(LOC), - RecordType::MX => read_record!(MX), - RecordType::NAPTR => read_record!(NAPTR), - RecordType::NS => read_record!(NS), - RecordType::OPENPGPKEY => read_record!(OPENPGPKEY), - RecordType::PTR => read_record!(PTR), - RecordType::SSHFP => read_record!(SSHFP), - RecordType::SOA => read_record!(SOA), - RecordType::SRV => read_record!(SRV), - RecordType::TLSA => read_record!(TLSA), - RecordType::TXT => read_record!(TXT), - RecordType::URI => read_record!(URI), - - RecordType::Other(type_number) => { - let mut bytes = Vec::new(); - for _ in 0 .. len { - bytes.push(c.read_u8()?); - } - - Ok(Self::Other { type_number, bytes }) - } - } - } -} - - -impl QClass { - fn from_u16(uu: u16) -> Self { - match uu { - 0x0001 => Self::IN, - 0x0003 => Self::CH, - 0x0004 => Self::HS, - _ => Self::Other(uu), - } - } - - fn to_u16(self) -> u16 { - match self { - Self::IN => 0x0001, - Self::CH => 0x0003, - Self::HS => 0x0004, - Self::Other(uu) => uu, - } - } -} - - -impl Flags { - - /// The set of flags that represents a query packet. - pub fn query() -> Self { - Self::from_u16(0b_0000_0001_0000_0000) - } - - /// The set of flags that represents a successful response. - pub fn standard_response() -> Self { - Self::from_u16(0b_1000_0001_1000_0000) - } - - /// Converts the flags into a two-byte number. - pub fn to_u16(self) -> u16 { // 0123 4567 89AB CDEF - let mut bits = 0b_0000_0000_0000_0000; - if self.response { bits |= 0b_1000_0000_0000_0000; } - match self.opcode { - Opcode::Query => { bits |= 0b_0000_0000_0000_0000; } - Opcode::Other(_) => { unimplemented!(); } - } - if self.authoritative { bits |= 0b_0000_0100_0000_0000; } - if self.truncated { bits |= 0b_0000_0010_0000_0000; } - if self.recursion_desired { bits |= 0b_0000_0001_0000_0000; } - if self.recursion_available { bits |= 0b_0000_0000_1000_0000; } - // (the Z bit is reserved) 0b_0000_0000_0100_0000 - if self.authentic_data { bits |= 0b_0000_0000_0010_0000; } - if self.checking_disabled { bits |= 0b_0000_0000_0001_0000; } - - bits - } - - /// Extracts the flags from the given two-byte number. - pub fn from_u16(bits: u16) -> Self { - let has_bit = |bit| { bits & bit == bit }; - - Self { - response: has_bit(0b_1000_0000_0000_0000), - opcode: Opcode::from_bits((bits.to_be_bytes()[0] & 0b_0111_1000) >> 3), - authoritative: has_bit(0b_0000_0100_0000_0000), - truncated: has_bit(0b_0000_0010_0000_0000), - recursion_desired: has_bit(0b_0000_0001_0000_0000), - recursion_available: has_bit(0b_0000_0000_1000_0000), - authentic_data: has_bit(0b_0000_0000_0010_0000), - checking_disabled: has_bit(0b_0000_0000_0001_0000), - error_code: ErrorCode::from_bits(bits & 0b_1111), - } - } -} - - -impl Opcode { - - /// Extracts the opcode from this four-bit number, which should have been - /// extracted from the packet and shifted to be in the range 0–15. - fn from_bits(bits: u8) -> Self { - if bits == 0 { - Self::Query - } - else { - assert!(bits <= 15, "bits {:#08b} out of range", bits); - Self::Other(bits) - } - } -} - - -impl ErrorCode { - - /// Extracts the rcode from the last four bits of the flags field. - fn from_bits(bits: u16) -> Option { - if (0x0F01 .. 0x0FFF).contains(&bits) { - return Some(Self::Private(bits)); - } - - match bits { - 0 => None, - 1 => Some(Self::FormatError), - 2 => Some(Self::ServerFailure), - 3 => Some(Self::NXDomain), - 4 => Some(Self::NotImplemented), - 5 => Some(Self::QueryRefused), - 16 => Some(Self::BadVersion), - n => Some(Self::Other(n)), - } - } -} - - -/// Trait for decoding DNS record structures from bytes read over the wire. -pub trait Wire: Sized { - - /// This record’s type as a string, such as `"A"` or `"CNAME"`. - const NAME: &'static str; - - /// The number signifying that a record is of this type. - /// See - const RR_TYPE: u16; - - /// Read at most `len` bytes from the given `Cursor`. This cursor travels - /// throughout the complete data — by this point, we have read the entire - /// response into a buffer. - fn read(len: u16, c: &mut Cursor<&[u8]>) -> Result; -} - - -/// Something that can go wrong deciphering a record. -#[derive(PartialEq, Debug)] -pub enum WireError { - - /// There was an IO error reading from the cursor. - /// Almost all the time, this means that the buffer was too short. - IO, - // (io::Error is not PartialEq so we don’t propagate it) - - /// When the DNS standard requires records of this type to have a certain - /// fixed length, but the response specified a different length. - /// - /// This error should be returned regardless of the _content_ of the - /// record, whatever it is. - WrongRecordLength { - - /// The length of the record’s data, as specified in the packet. - stated_length: u16, - - /// The length of the record that the DNS specification mandates. - mandated_length: MandatedLength, - }, - - /// When the length of this record as specified in the packet differs from - /// the computed length, as determined by reading labels. - /// - /// There are two ways, in general, to read arbitrary-length data from a - /// stream of bytes: length-prefixed (read the length, then read that many - /// bytes) or sentinel-terminated (keep reading bytes until you read a - /// certain value, usually zero). The DNS protocol uses both: each - /// record’s size is specified up-front in the packet, but inside the - /// record, there exist arbitrary-length strings that must be read until a - /// zero is read, indicating there is no more string. - /// - /// Consider the case of a packet, with a specified length, containing a - /// string of arbitrary length (such as the CNAME or TXT records). A DNS - /// client has to deal with this in one of two ways: - /// - /// 1. Read exactly the specified length of bytes from the record, raising - /// an error if the contents are too short or a string keeps going past - /// the length (assume the length is correct but the contents are wrong). - /// - /// 2. Read as many bytes from the record as the string requests, raising - /// an error if the number of bytes read at the end differs from the - /// expected length of the record (assume the length is wrong but the - /// contents are correct). - /// - /// Note that no matter which way is picked, the record will still be - /// incorrect — it only impacts the parsing of records that occur after it - /// in the packet. Knowing which method should be used requires knowing - /// what caused the DNS packet to be erroneous, which we cannot know. - /// - /// dog picks the second way. If a record ends up reading more or fewer - /// bytes than it is ‘supposed’ to, it will raise this error, but _after_ - /// having read a different number of bytes than the specified length. - WrongLabelLength { - - /// The length of the record’s data, as specified in the packet. - stated_length: u16, - - /// The computed length of the record’s data, based on the number of - /// bytes consumed by reading labels from the packet. - length_after_labels: u16, - }, - - /// When the data contained a string containing a cycle of pointers. - /// Contains the vector of indexes that was being checked. - TooMuchRecursion(Box<[u16]>), - - /// When the data contained a string with a pointer to an index outside of - /// the packet. Contains the invalid index. - OutOfBounds(u16), - - /// When a record in the packet contained a version field that specifies - /// the format of its remaining fields, but this version is too recent to - /// be supported, so we cannot parse it. - WrongVersion { - - /// The version of the record layout, as specified in the packet - stated_version: u8, - - /// The maximum version that this version of dog supports. - maximum_supported_version: u8, - } -} - -/// The rule for how long a record in a packet should be. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum MandatedLength { - - /// The record should be exactly this many bytes in length. - Exactly(u16), - - /// The record should be _at least_ this many bytes in length. - AtLeast(u16), -} - -impl From for WireError { - fn from(ioe: io::Error) -> Self { - error!("IO error -> {:?}", ioe); - Self::IO - } -} diff --git a/dns/tests/wire_building_tests.rs b/dns/tests/wire_building_tests.rs deleted file mode 100644 index d8de251..0000000 --- a/dns/tests/wire_building_tests.rs +++ /dev/null @@ -1,41 +0,0 @@ -use dns::{Request, Flags, Query, Labels, QClass}; -use dns::record::RecordType; - -use pretty_assertions::assert_eq; - - -#[test] -fn build_request() { - let request = Request { - transaction_id: 0xceac, - flags: Flags::query(), - query: Query { - qname: Labels::encode("rfcs.io").unwrap(), - qclass: QClass::Other(0x42), - qtype: RecordType::from(0x1234), - }, - additional: Some(Request::additional_record()), - }; - - let result = vec![ - 0xce, 0xac, // transaction ID - 0x01, 0x00, // flags (standard query) - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // counts (1, 0, 0, 1) - - // query: - 0x04, 0x72, 0x66, 0x63, 0x73, 0x02, 0x69, 0x6f, 0x00, // qname - 0x12, 0x34, // type - 0x00, 0x42, // class - - // OPT record: - 0x00, // name - 0x00, 0x29, // type OPT - 0x02, 0x00, // UDP payload size - 0x00, // higher bits - 0x00, // EDNS(0) version - 0x00, 0x00, // more flags - 0x00, 0x00, // no data - ]; - - assert_eq!(request.to_bytes().unwrap(), result); -} diff --git a/dns/tests/wire_parsing_tests.rs b/dns/tests/wire_parsing_tests.rs deleted file mode 100644 index 7939a56..0000000 --- a/dns/tests/wire_parsing_tests.rs +++ /dev/null @@ -1,271 +0,0 @@ -use std::net::Ipv4Addr; - -use dns::{Response, Query, Answer, Labels, Flags, Opcode, QClass}; -use dns::record::{Record, A, CNAME, OPT, SOA, UnknownQtype, RecordType}; - -use pretty_assertions::assert_eq; - - -#[test] -fn parse_nothing() { - assert!(Response::from_bytes(&[]).is_err()); -} - - -#[test] -fn parse_response_standard() { - let buf = &[ - 0x0d, 0xcd, // transaction ID - 0x81, 0x80, // flags (standard query, response, no error) - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, // counts (1, 1, 0, 1) - - // the query: - 0x03, 0x64, 0x6e, 0x73, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, - 0x64, 0x6f, 0x67, 0x00, // "dns.lookup.dog." - 0x00, 0x01, // type A - 0x00, 0x01, // class IN - - // the answer: - 0xc0, 0x0c, // to find the name, backtrack to position 0x0c (12) - 0x00, 0x01, // type A - 0x00, 0x01, // class IN - 0x00, 0x00, 0x03, 0xa5, // TTL (933 seconds) - 0x00, 0x04, // record data length 4 - 0x8a, 0x44, 0x75, 0x5e, // record date (138.68.117.94) - - // the additional: - 0x00, // no name - 0x00, 0x29, // type OPT - 0x02, 0x00, // UDP payload size (512) - 0x00, 0x00, // higher bits (all 0) - 0x00, // EDNS version - 0x00, 0x00, // extra bits (DO bit unset) - 0x00, // data length 0 - ]; - - let response = Response { - transaction_id: 0x0dcd, - flags: Flags { - response: true, - opcode: Opcode::Query, - authoritative: false, - truncated: false, - recursion_desired: true, - recursion_available: true, - authentic_data: false, - checking_disabled: false, - error_code: None, - }, - queries: vec![ - Query { - qname: Labels::encode("dns.lookup.dog").unwrap(), - qclass: QClass::IN, - qtype: RecordType::A, - }, - ], - answers: vec![ - Answer::Standard { - qname: Labels::encode("dns.lookup.dog").unwrap(), - qclass: QClass::IN, - ttl: 933, - record: Record::A(A { - address: Ipv4Addr::new(138, 68, 117, 94), - }), - } - ], - authorities: vec![], - additionals: vec![ - Answer::Pseudo { - qname: Labels::root(), - opt: OPT { - udp_payload_size: 512, - higher_bits: 0, - edns0_version: 0, - flags: 0, - data: vec![], - }, - }, - ], - }; - - assert_eq!(Response::from_bytes(buf), Ok(response)); -} - - -#[test] -fn parse_response_with_mixed_string() { - let buf = &[ - 0x06, 0x9f, // transaction ID - 0x81, 0x80, // flags (standard query, response, no error) - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // counts (1, 1, 0, 0) - - // the query: - 0x0d, 0x63, 0x6e, 0x61, 0x6d, 0x65, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x03, 0x64, 0x6f, - 0x67, 0x00, // "cname-example.lookup.dog" - 0x00, 0x05, // type CNAME - 0x00, 0x01, // class IN - - // the answer: - 0xc0, 0x0c, // to find the name, backtrack to position 0x0c (12) - 0x00, 0x05, // type CNAME - 0x00, 0x01, // class IN - 0x00, 0x00, 0x03, 0x69, // TTL (873 seconds) - 0x00, 0x06, // record data length 6 - 0x03, 0x64, 0x6e, 0x73, 0xc0, 0x1a, - // "dns.lookup.dog.", which is "dns." + backtrack to position 0x1a (28) - ]; - - let response = Response { - transaction_id: 0x069f, - flags: Flags { - response: true, - opcode: Opcode::Query, - authoritative: false, - truncated: false, - recursion_desired: true, - recursion_available: true, - authentic_data: false, - checking_disabled: false, - error_code: None, - }, - queries: vec![ - Query { - qname: Labels::encode("cname-example.lookup.dog").unwrap(), - qclass: QClass::IN, - qtype: RecordType::CNAME, - }, - ], - answers: vec![ - Answer::Standard { - qname: Labels::encode("cname-example.lookup.dog").unwrap(), - qclass: QClass::IN, - ttl: 873, - record: Record::CNAME(CNAME { - domain: Labels::encode("dns.lookup.dog").unwrap(), - }), - } - ], - authorities: vec![], - additionals: vec![], - }; - - assert_eq!(Response::from_bytes(buf), Ok(response)); -} - - -#[test] -fn parse_response_with_multiple_additionals() { - - // This is an artifical amalgam of DNS, not a real-world response! - let buf = &[ - 0xce, 0xac, // transaction ID - 0x81, 0x80, // flags (standard query, response, no error) - 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, // counts (1, 1, 1, 2) - - // query: - 0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, 0x00, // name - 0x00, 0x01, // type A - 0x00, 0x01, // class IN - - // answer: - 0xc0, 0x0c, // name (backreference) - 0x00, 0x01, // type A - 0x00, 0x01, // class IN - 0x00, 0x00, 0x03, 0x77, // TTL - 0x00, 0x04, // data length 4 - 0x8a, 0x44, 0x75, 0x5e, // IP address - - // authoritative: - 0x00, // name - 0x00, 0x06, // type SOA - 0x00, 0x01, // class IN - 0xFF, 0xFF, 0xFF, 0xFF, // TTL (maximum possible!) - 0x00, 0x1B, // data length - 0x01, 0x61, 0x00, // primary name server ("a") - 0x02, 0x6d, 0x78, 0x00, // mailbox ("mx") - 0x78, 0x68, 0x52, 0x2c, // serial number - 0x00, 0x00, 0x07, 0x08, // refresh interval - 0x00, 0x00, 0x03, 0x84, // retry interval - 0x00, 0x09, 0x3a, 0x80, // expire limit - 0x00, 0x01, 0x51, 0x80, // minimum TTL - - // additional 1: - 0x00, // name - 0x00, 0x99, // unknown type - 0x00, 0x99, // unknown class - 0x12, 0x34, 0x56, 0x78, // TTL - 0x00, 0x04, // data length 4 - 0x12, 0x34, 0x56, 0x78, // data - - // additional 2: - 0x00, // name - 0x00, 0x29, // type OPT - 0x02, 0x00, // UDP payload size - 0x00, // higher bits - 0x00, // EDNS(0) version - 0x00, 0x00, // more flags - 0x00, 0x00, // no data - ]; - - let response = Response { - transaction_id: 0xceac, - flags: Flags::standard_response(), - queries: vec![ - Query { - qname: Labels::encode("bsago.me").unwrap(), - qclass: QClass::IN, - qtype: RecordType::A, - }, - ], - answers: vec![ - Answer::Standard { - qname: Labels::encode("bsago.me").unwrap(), - qclass: QClass::IN, - ttl: 887, - record: Record::A(A { - address: Ipv4Addr::new(138, 68, 117, 94), - }), - } - ], - authorities: vec![ - Answer::Standard { - qname: Labels::root(), - qclass: QClass::IN, - ttl: 4294967295, - record: Record::SOA(SOA { - mname: Labels::encode("a").unwrap(), - rname: Labels::encode("mx").unwrap(), - serial: 2020102700, - refresh_interval: 1800, - retry_interval: 900, - expire_limit: 604800, - minimum_ttl: 86400, - }), - } - ], - additionals: vec![ - Answer::Standard { - qname: Labels::root(), - qclass: QClass::Other(153), - ttl: 305419896, - record: Record::Other { - type_number: UnknownQtype::UnheardOf(153), - bytes: vec![ 0x12, 0x34, 0x56, 0x78 ], - }, - }, - Answer::Pseudo { - qname: Labels::root(), - opt: OPT { - udp_payload_size: 512, - higher_bits: 0, - edns0_version: 0, - flags: 0, - data: vec![], - }, - }, - ], - }; - - assert_eq!(Response::from_bytes(buf), Ok(response)); -} diff --git a/src/hints.rs b/src/hints.rs index cbd9541..92e97aa 100644 --- a/src/hints.rs +++ b/src/hints.rs @@ -16,7 +16,7 @@ use log::*; /// case, to prevent confusion. #[derive(Default)] pub struct LocalHosts { - hostnames: BTreeSet, + hostnames: BTreeSet, } impl LocalHosts { @@ -64,7 +64,7 @@ impl LocalHosts { } for hostname in line.split_ascii_whitespace().skip(1) { - match dns::Labels::encode(hostname) { + match doge_dns::Labels::encode(hostname) { Ok(hn) => { hostnames.insert(hn); } @@ -81,7 +81,7 @@ impl LocalHosts { /// Queries this set of hostnames to see if the given name, which is about /// to be queried for, exists within the file. - pub fn contains(&self, hostname_in_query: &dns::Labels) -> bool { + pub fn contains(&self, hostname_in_query: &doge_dns::Labels) -> bool { self.hostnames.contains(hostname_in_query) } } diff --git a/src/main.rs b/src/main.rs index bd7646b..d869b87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,9 +142,9 @@ fn run(Options { requests, format, measure_time }: Options) -> i32 { } if ! should_show_opt { - response.answers.retain(dns::Answer::is_standard); - response.authorities.retain(dns::Answer::is_standard); - response.additionals.retain(dns::Answer::is_standard); + response.answers.retain(doge_dns::Answer::is_standard); + response.authorities.retain(doge_dns::Answer::is_standard); + response.additionals.retain(doge_dns::Answer::is_standard); } responses.push(response); diff --git a/src/options.rs b/src/options.rs index e99e6e8..ad3764a 100644 --- a/src/options.rs +++ b/src/options.rs @@ -5,8 +5,8 @@ use std::fmt; use log::*; -use dns::{QClass, Labels}; -use dns::record::RecordType; +use doge_dns::{QClass, Labels}; +use doge_dns::record::RecordType; use crate::connect::{PortNumber, TransportType}; use crate::output::{OutputFormat, UseColours, TextFormat}; @@ -522,7 +522,7 @@ impl fmt::Display for OptionsError { mod test { use super::*; use pretty_assertions::assert_eq; - use dns::record::UnknownQtype; + use doge_dns::record::UnknownQtype; impl Inputs { fn fallbacks() -> Self { diff --git a/src/output.rs b/src/output.rs index ce41354..7221beb 100644 --- a/src/output.rs +++ b/src/output.rs @@ -4,8 +4,8 @@ use std::fmt; use std::time::Duration; use std::env; -use dns::{Response, Query, Answer, QClass, ErrorCode, WireError, MandatedLength}; -use dns::record::{Record, RecordType, UnknownQtype, OPT}; +use doge_dns::{Response, Query, Answer, QClass, ErrorCode, WireError, MandatedLength}; +use doge_dns::record::{Record, RecordType, UnknownQtype, OPT}; use dns_transport::Error as TransportError; use json::{object, JsonValue}; diff --git a/src/requests.rs b/src/requests.rs index 71c244c..2a0f8ed 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -29,13 +29,13 @@ pub struct RequestGenerator { pub struct Inputs { /// The list of domain names to query. - pub domains: Vec, + pub domains: Vec, /// The list of DNS record types to query for. - pub record_types: Vec, + pub record_types: Vec, /// The list of DNS classes to query for. - pub classes: Vec, + pub classes: Vec, /// The list of resolvers to send queries to. pub resolver_types: Vec, @@ -84,7 +84,7 @@ pub enum UseEDNS { /// The entry type for `RequestGenerator`: a transport to send a request, and /// a list of one or more DNS queries to send over it, as determined by the /// search path in the resolver. -pub type RequestSet = (Box, Vec); +pub type RequestSet = (Box, Vec); impl RequestGenerator { @@ -103,12 +103,12 @@ impl RequestGenerator { for resolver in &resolvers { for transport_type in &self.inputs.transport_types { - let mut flags = dns::Flags::query(); + let mut flags = doge_dns::Flags::query(); self.protocol_tweaks.set_request_flags(&mut flags); let mut additional = None; if self.edns.should_send() { - let mut opt = dns::Request::additional_record(); + let mut opt = doge_dns::Request::additional_record(); self.protocol_tweaks.set_request_opt_fields(&mut opt); additional = Some(opt); } @@ -128,8 +128,8 @@ impl RequestGenerator { let mut request_list = Vec::new(); for qname in resolver.name_list(domain) { let transaction_id = self.txid_generator.generate(); - let query = dns::Query { qname, qclass, qtype }; - let request = dns::Request { transaction_id, flags, query, additional: additional.clone() }; + let query = doge_dns::Query { qname, qclass, qtype }; + let request = doge_dns::Request { transaction_id, flags, query, additional: additional.clone() }; request_list.push(request); } requests.push((transport, request_list)); @@ -159,7 +159,7 @@ impl UseEDNS { impl ProtocolTweaks { /// Sets fields in the DNS flags based on the user’s requested tweaks. - pub fn set_request_flags(self, flags: &mut dns::Flags) { + pub fn set_request_flags(self, flags: &mut doge_dns::Flags) { if self.set_authoritative_flag { flags.authoritative = true; } @@ -175,7 +175,7 @@ impl ProtocolTweaks { /// Set the payload size field in the outgoing OPT record, if the user has /// requested to do so. - pub fn set_request_opt_fields(self, opt: &mut dns::record::OPT) { + pub fn set_request_opt_fields(self, opt: &mut doge_dns::record::OPT) { if let Some(bufsize) = self.udp_payload_size { opt.udp_payload_size = bufsize; } diff --git a/src/resolve.rs b/src/resolve.rs index 29d90bd..e5b35bf 100644 --- a/src/resolve.rs +++ b/src/resolve.rs @@ -5,7 +5,7 @@ use std::io; use log::*; -use dns::Labels; +use doge_dns::Labels; /// A **resolver type** is the source of a `Resolver`. diff --git a/src/table.rs b/src/table.rs index 7455fd6..be0ea3c 100644 --- a/src/table.rs +++ b/src/table.rs @@ -4,8 +4,8 @@ use std::time::Duration; use ansi_term::ANSIString; -use dns::Answer; -use dns::record::Record; +use doge_dns::Answer; +use doge_dns::record::Record; use crate::colours::Colours; use crate::output::TextFormat; From 974823fb4ec54cdfc3cd30bbb0c2d08e3b490d3f Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Tue, 20 Feb 2024 01:09:38 -0600 Subject: [PATCH 03/10] Fixing private types in **NEW** doge_dns crate --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- dns-transport/Cargo.toml | 2 +- src/output.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3a4dfc..1e6368c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "doge_dns" -version = "0.2.4-beta" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c198586e6649caf93c0932a982a7036dd4b34f1c47da1f9b669f872f98bb9405" +checksum = "d0b233996a83c2486f7a291dbdd75ae23fd0f6d8f61d6376e9452da80b96d185" dependencies = [ "base64 0.21.7", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 1569118..346ebb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ panic = "abort" [dependencies] # dns stuff -doge_dns = "0.2.4-beta" +doge_dns = "1.0.1" dns-transport = { path = "./dns-transport" } # command-line diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml index 5a134b1..6395bfb 100644 --- a/dns-transport/Cargo.toml +++ b/dns-transport/Cargo.toml @@ -12,7 +12,7 @@ test = false [dependencies] # dns wire protocol -doge_dns = "0.2.4-beta" +doge_dns = "1.0.0" # logging log = "0.4" diff --git a/src/output.rs b/src/output.rs index 7221beb..8d74bf1 100644 --- a/src/output.rs +++ b/src/output.rs @@ -682,7 +682,7 @@ fn error_message(error: TransportError) -> String { /// Formats a wire error into its human-readable message, describing what was /// wrong with the packet we received. -fn wire_error_message(error: WireError) -> String { +fn wire_error_message(error: doge_dns::WireError) -> String { match error { WireError::IO => { "Malformed packet: insufficient data".into() From 2501bdb122cebe4ebb5fcb9ffba1e02d669006c5 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 14:40:18 -0600 Subject: [PATCH 04/10] testing publishing with sub lib in progect --- Cargo.lock | 2 +- Cargo.toml | 2 +- dns-transport/Cargo.toml | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e6368c..5a11b2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dns-transport" -version = "0.2.4-beta" +version = "0.2.4" dependencies = [ "cfg-if", "doge_dns", diff --git a/Cargo.toml b/Cargo.toml index 346ebb6..0845fe8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ panic = "abort" # dns stuff doge_dns = "1.0.1" -dns-transport = { path = "./dns-transport" } +dns-transport = { path = "./dns-transport", version = "2.4" } # command-line ansi_term = "0.12" diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml index 6395bfb..cfa303c 100644 --- a/dns-transport/Cargo.toml +++ b/dns-transport/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "dns-transport" -version = "0.2.4-beta" -authors = ["Benjamin Sago "] +version = "0.2.4" +authors = ["Darrion Whitfield "] +description = "The doge lib for preparing and communicating with dns servers and protocols" edition = "2018" [lib] From 195f71d83bdea9479244715a280dc33e33cf131c Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 14:53:06 -0600 Subject: [PATCH 05/10] cargo tweaks --- Cargo.lock | 11 ++++------- Cargo.toml | 2 +- dns-transport/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a11b2b..e3972ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "a3b1be7772ee4501dba05acbe66bb1e8760f6a6c474a36035631638e4415f130" [[package]] name = "byteorder" @@ -60,12 +60,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" [[package]] name = "cfg-if" diff --git a/Cargo.toml b/Cargo.toml index 0845fe8..d8bf7e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ panic = "abort" # dns stuff doge_dns = "1.0.1" -dns-transport = { path = "./dns-transport", version = "2.4" } +dns-transport = { path = "./dns-transport", version = "0.2.4" } # command-line ansi_term = "0.12" diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml index cfa303c..c3dd70f 100644 --- a/dns-transport/Cargo.toml +++ b/dns-transport/Cargo.toml @@ -13,7 +13,7 @@ test = false [dependencies] # dns wire protocol -doge_dns = "1.0.0" +doge_dns = "1.0.1" # logging log = "0.4" From 20b63ba14e11e74cb80afd2af1f3764bf1920b76 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 17:56:59 -0600 Subject: [PATCH 06/10] Moved both dns && dns-transport to crates.io as doge_dns and doge_transport respectively --- Cargo.lock | 34 ++++---- Cargo.toml | 22 ++--- dns-transport/Cargo.toml | 43 ---------- dns-transport/src/auto.rs | 59 -------------- dns-transport/src/error.rs | 87 -------------------- dns-transport/src/https.rs | 140 -------------------------------- dns-transport/src/lib.rs | 80 ------------------ dns-transport/src/tcp.rs | 137 ------------------------------- dns-transport/src/tls.rs | 86 -------------------- dns-transport/src/tls_stream.rs | 69 ---------------- dns-transport/src/udp.rs | 64 --------------- src/connect.rs | 2 +- src/output.rs | 2 +- src/requests.rs | 4 +- 14 files changed, 33 insertions(+), 796 deletions(-) delete mode 100644 dns-transport/Cargo.toml delete mode 100644 dns-transport/src/auto.rs delete mode 100644 dns-transport/src/error.rs delete mode 100644 dns-transport/src/https.rs delete mode 100644 dns-transport/src/lib.rs delete mode 100644 dns-transport/src/tcp.rs delete mode 100644 dns-transport/src/tls.rs delete mode 100644 dns-transport/src/tls_stream.rs delete mode 100644 dns-transport/src/udp.rs diff --git a/Cargo.lock b/Cargo.lock index e3972ab..1d2d643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,29 +113,15 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "dns-transport" -version = "0.2.4" -dependencies = [ - "cfg-if", - "doge_dns", - "httparse", - "log", - "native-tls", - "rustls", - "webpki 0.22.4", - "webpki-roots", -] - [[package]] name = "doge" -version = "0.2.4-beta" +version = "0.2.5-beta" dependencies = [ "ansi_term", "atty", "datetime", - "dns-transport", "doge_dns", + "doge_transport", "getopts", "ipconfig", "json", @@ -156,6 +142,22 @@ dependencies = [ "unic-idna", ] +[[package]] +name = "doge_transport" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8492883e7105b8f11c0b974d977ffecd535263462c24e864b2e913362315987b" +dependencies = [ + "cfg-if", + "doge_dns", + "httparse", + "log", + "native-tls", + "rustls", + "webpki 0.22.4", + "webpki-roots", +] + [[package]] name = "errno" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index d8bf7e9..aaf1fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ exclude = [ homepage = "https://dns.lookup.dog/" license = "MIT" -version = "0.2.4-beta" +version = "0.2.5-beta" [[bin]] @@ -21,10 +21,10 @@ path = "src/main.rs" doctest = false -[workspace] -members = [ - "dns-transport", -] +#[workspace] +#members = [ +# "dns-transport", +#] # make dev builds faster by excluding debug symbols @@ -42,7 +42,7 @@ panic = "abort" # dns stuff doge_dns = "1.0.1" -dns-transport = { path = "./dns-transport", version = "0.2.4" } +doge_transport = "0.2.4" # command-line ansi_term = "0.12" @@ -72,9 +72,9 @@ pretty_assertions = "0.7" default = ["with_idna", "with_tls", "with_https", "with_nativetls"] with_idna = ["doge_dns/with_idna"] -with_tls = ["dns-transport/with_tls"] -with_https = ["dns-transport/with_https"] +with_tls = ["doge_transport/with_tls"] +with_https = ["doge_transport/with_https"] -with_nativetls = ["dns-transport/with_nativetls"] -with_nativetls_vendored = ["with_nativetls", "dns-transport/with_nativetls", "dns-transport/with_nativetls_vendored"] -with_rustls = ["dns-transport/with_rustls"] +with_nativetls = ["doge_transport/with_nativetls"] +with_nativetls_vendored = ["with_nativetls", "doge_transport/with_nativetls", "doge_transport/with_nativetls_vendored"] +with_rustls = ["doge_transport/with_rustls"] diff --git a/dns-transport/Cargo.toml b/dns-transport/Cargo.toml deleted file mode 100644 index c3dd70f..0000000 --- a/dns-transport/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "dns-transport" -version = "0.2.4" -authors = ["Darrion Whitfield "] -description = "The doge lib for preparing and communicating with dns servers and protocols" -edition = "2018" - -[lib] -doctest = false -test = false - - -[dependencies] - -# dns wire protocol -doge_dns = "1.0.1" - -# logging -log = "0.4" - -# tls networking -native-tls = { version = "0.2", optional = true } - -# http response parsing -httparse = { version = "1.3", optional = true } - -rustls = { version = "0.19", optional = true } - -webpki = { version = "0.22.4", optional = true } - -webpki-roots = { version = "0.26.1", optional = true } - -cfg-if = "1" - -[features] -default = [] # these are enabled in the main dog crate - -with_tls = [] -with_https = ["httparse"] - -with_nativetls = ["native-tls"] -with_nativetls_vendored = ["native-tls", "native-tls/vendored"] -with_rustls = ["rustls", "webpki-roots", "webpki"] diff --git a/dns-transport/src/auto.rs b/dns-transport/src/auto.rs deleted file mode 100644 index 6487ab9..0000000 --- a/dns-transport/src/auto.rs +++ /dev/null @@ -1,59 +0,0 @@ -use log::*; - -use doge_dns::{Request, Response}; -use crate::GenericTransport; - -use super::{Transport, Error, UdpTransport, TcpTransport}; - - -/// The **automatic transport**, which sends DNS wire data using the UDP -/// transport, then tries using the TCP transport if the first one fails -/// because the response wouldn’t fit in a single UDP packet. -/// -/// This is the default behaviour for many DNS clients. -pub struct AutoTransport { - addr: String, - port: u16 -} - -impl AutoTransport { - - /// Creates a new automatic transport that connects to the given host. - pub fn new(addr: GenericTransport) -> Self { - if addr.port_num != 0 { - Self { - addr : addr.address, - port : addr.port_num, - } - } else { - Self { - addr : addr.address, - port : 53 - } - } - } -} - - -impl Transport for AutoTransport { - fn send(&self, request: &Request) -> Result { - let udp_transport = UdpTransport::new(GenericTransport { - address: self.addr.clone(), - port_num: self.port.clone(), - }); - let udp_response = udp_transport.send(&request)?; - - if ! udp_response.flags.truncated { - return Ok(udp_response); - } - - debug!("Truncated flag set, so switching to TCP"); - - let tcp_transport = TcpTransport::new(GenericTransport { - address: self.addr.clone(), - port_num: self.port.clone(), - }); - let tcp_response = tcp_transport.send(&request)?; - Ok(tcp_response) - } -} diff --git a/dns-transport/src/error.rs b/dns-transport/src/error.rs deleted file mode 100644 index 2fda52b..0000000 --- a/dns-transport/src/error.rs +++ /dev/null @@ -1,87 +0,0 @@ -/// Something that can go wrong making a DNS request. -#[derive(Debug)] -pub enum Error { - - /// The data in the response did not parse correctly from the DNS wire - /// protocol format. - WireError(doge_dns::WireError), - - /// There was a problem with the network making a TCP or UDP request. - NetworkError(std::io::Error), - - /// Not enough information was received from the server before a `read` - /// call returned zero bytes. - TruncatedResponse, - - /// There was a problem making a TLS request. - #[cfg(feature = "with_nativetls")] - TlsError(native_tls::Error), - - /// There was a problem _establishing_ a TLS request. - #[cfg(feature = "with_nativetls")] - TlsHandshakeError(native_tls::HandshakeError), - - /// Provided dns name is not valid - #[cfg(feature = "with_rustls")] - RustlsInvalidDnsNameError(webpki::InvalidDNSNameError), - - /// There was a problem decoding the response HTTP headers or body. - #[cfg(feature = "with_https")] - HttpError(httparse::Error), - - /// The HTTP response code was something other than 200 OK, along with the - /// response code text, if present. - #[cfg(feature = "with_https")] - WrongHttpStatus(u16, Option), - - /// If an error occours while parsing the port number. - PortError(std::num::ParseIntError), -} - - -// From impls -impl From for Error { - fn from(inner: std::num::ParseIntError) -> Self { - Self::PortError(inner) - } -} - -impl From for Error { - fn from(inner: doge_dns::WireError) -> Self { - Self::WireError(inner) - } -} - -impl From for Error { - fn from(inner: std::io::Error) -> Self { - Self::NetworkError(inner) - } -} - -#[cfg(feature = "with_nativetls")] -impl From for Error { - fn from(inner: native_tls::Error) -> Self { - Self::TlsError(inner) - } -} - -#[cfg(feature = "with_nativetls")] -impl From> for Error { - fn from(inner: native_tls::HandshakeError) -> Self { - Self::TlsHandshakeError(inner) - } -} - -#[cfg(feature = "with_rustls")] -impl From for Error { - fn from(inner: webpki::InvalidDNSNameError) -> Self { - Self::RustlsInvalidDnsNameError(inner) - } -} - -#[cfg(feature = "with_https")] -impl From for Error { - fn from(inner: httparse::Error) -> Self { - Self::HttpError(inner) - } -} diff --git a/dns-transport/src/https.rs b/dns-transport/src/https.rs deleted file mode 100644 index 7971d79..0000000 --- a/dns-transport/src/https.rs +++ /dev/null @@ -1,140 +0,0 @@ -#![cfg_attr(not(feature = "https"), allow(unused))] - -use std::io::{Read, Write}; -use std::net::TcpStream; - -use log::*; - -use doge_dns::{Request, Response, WireError}; -use crate::GenericTransport; - -use super::{Transport, Error}; - -use super::tls_stream; - -/// The **HTTPS transport**, which sends DNS wire data inside HTTP packets -/// encrypted with TLS, using TCP. -pub struct HttpsTransport { - url: String, - port: u16 -} - -impl HttpsTransport { - - /// Creates a new HTTPS transport that connects to the given URL. - pub fn new(addr: GenericTransport) -> Self { - if addr.port_num != 0 { - Self { - url : addr.address, - port : addr.port_num, - } - } else { - Self { - url : addr.address, - port : 443 - } - } - } -} - -fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option { - haystack.windows(needle.len()).position(|window| window == needle) -} - -fn contains_header(buf: &[u8]) -> bool { - let header_end: [u8; 4] = [ 13, 10, 13, 10 ]; - find_subsequence(buf, &header_end).is_some() -} - -use tls_stream::TlsStream; - -impl Transport for HttpsTransport { - - #[cfg(any(feature = "with_https"))] - fn send(&self, request: &Request) -> Result { - let (domain, path) = self.split_domain().expect("Invalid HTTPS nameserver"); - - info!("Opening TLS socket to {:?}", domain); - let mut stream = Self::stream(&domain, self.port)?; - - debug!("Connected"); - - let request_bytes = request.to_bytes().expect("failed to serialise request"); - let mut bytes_to_send = format!("\ - POST {} HTTP/1.1\r\n\ - Host: {}\r\n\ - Content-Type: application/dns-message\r\n\ - Accept: application/dns-message\r\n\ - User-Agent: {}\r\n\ - Content-Length: {}\r\n\r\n", - path, domain, USER_AGENT, request_bytes.len()).into_bytes(); - bytes_to_send.extend(request_bytes); - - info!("Sending {} bytes of data to {:?} over HTTPS", bytes_to_send.len(), self.url); - stream.write_all(&bytes_to_send)?; - debug!("Wrote all bytes"); - - info!("Waiting to receive..."); - let mut buf = [0; 4096]; - let mut read_len = stream.read(&mut buf)?; - while !contains_header(&buf[0..read_len]) { - if read_len == buf.len() { - return Err(Error::WireError(WireError::IO)); - } - read_len += stream.read(&mut buf[read_len..])?; - } - let mut expected_len = read_len; - info!("Received {} bytes of data", read_len); - - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut response = httparse::Response::new(&mut headers); - let index: usize = response.parse(&buf)?.unwrap(); - - if response.code != Some(200) { - let reason = response.reason.map(str::to_owned); - return Err(Error::WrongHttpStatus(response.code.unwrap(), reason)); - } - - for header in response.headers { - let str_value = String::from_utf8_lossy(header.value); - debug!("Header {:?} -> {:?}", header.name, str_value); - if header.name == "Content-Length" { - let content_length: usize = str_value.parse().unwrap(); - expected_len = index + content_length; - } - } - - while read_len < expected_len { - if read_len == buf.len() { - return Err(Error::WireError(WireError::IO)); - } - read_len += stream.read(&mut buf[read_len..])?; - } - - let body = &buf[index .. read_len]; - debug!("HTTP body has {} bytes", body.len()); - let response = Response::from_bytes(&body)?; - Ok(response) - } - - #[cfg(not(feature = "with_https"))] - fn send(&self, request: &Request) -> Result { - unreachable!("HTTPS feature disabled") - } -} - -impl HttpsTransport { - fn split_domain(&self) -> Option<(&str, &str)> { - if let Some(sp) = self.url.strip_prefix("https://") { - if let Some(colon_index) = sp.find('/') { - return Some((&sp[.. colon_index], &sp[colon_index ..])); - } - } - - None - } -} - -/// The User-Agent header sent with HTTPS requests. -static USER_AGENT: &str = concat!("doge/", env!("CARGO_PKG_VERSION")); - diff --git a/dns-transport/src/lib.rs b/dns-transport/src/lib.rs deleted file mode 100644 index e4ad83f..0000000 --- a/dns-transport/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! All the DNS transport types. - -#![warn(deprecated_in_future)] -#![warn(future_incompatible)] -#![warn(missing_copy_implementations)] -#![warn(missing_docs)] -#![warn(nonstandard_style)] -#![warn(rust_2018_compatibility)] -#![warn(rust_2018_idioms)] -#![warn(single_use_lifetimes)] -#![warn(trivial_casts, trivial_numeric_casts)] -#![warn(unused)] - -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::must_use_candidate)] -#![allow(clippy::option_if_let_else)] -#![allow(clippy::pub_enum_variant_names)] -#![allow(clippy::wildcard_imports)] - -#![deny(clippy::cast_possible_truncation)] -#![deny(clippy::cast_lossless)] -#![deny(clippy::cast_possible_wrap)] -#![deny(clippy::cast_sign_loss)] -#![deny(unsafe_code)] - - -mod auto; -pub use self::auto::AutoTransport; - -mod udp; -pub use self::udp::UdpTransport; - -mod tcp; -pub use self::tcp::TcpTransport; - -mod tls; -pub use self::tls::TlsTransport; - -mod https; -pub use self::https::HttpsTransport; - -mod error; - -mod tls_stream; - -pub use self::error::Error; - -/// The trait implemented by all transport types. -pub trait Transport { - - /// Convert the request to bytes, send it over the network, wait for a - /// response, deserialise it from bytes, and return it, asynchronously. - /// - /// # Errors - /// - /// Returns an `Error` error if there’s an I/O error sending or - /// receiving data, or the DNS packet in the response contained invalid - /// bytes and failed to parse, or if there was a protocol-level error for - /// the TLS and HTTPS transports. - fn send(&self, request: &doge_dns::Request) -> Result; -} - -/// The **Generic transport**, Allows easier passthrough of data to other transports -/// -/// # References -/// -/// - [RFC 1035 §4.2.2](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -/// - [RFC 7766](https://tools.ietf.org/html/rfc1035) — DNS Transport over -/// TCP, Implementation Requirements (March 2016) - -#[derive(Clone, Debug)] -pub struct GenericTransport { - /// The ** Address ** will be used as the ip v4/v6 address in **TCP, UDP and TLS** it will be used as the **URL** in DOH - pub address: String, - - /// Specifies sending queries to non standard ports when necessary - pub port_num: u16, -} \ No newline at end of file diff --git a/dns-transport/src/tcp.rs b/dns-transport/src/tcp.rs deleted file mode 100644 index be04da7..0000000 --- a/dns-transport/src/tcp.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::convert::TryFrom; -use std::net::TcpStream; -use std::io::{Read, Write}; - -use log::*; - -use doge_dns::{Request, Response}; -use crate::GenericTransport; - -use super::{Transport, Error}; - - -/// The **TCP transport**, which sends DNS wire data over a TCP stream. -/// -/// # References -/// -/// - [RFC 1035 §4.2.2](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -/// - [RFC 7766](https://tools.ietf.org/html/rfc1035) — DNS Transport over -/// TCP, Implementation Requirements (March 2016) -pub struct TcpTransport { - addr: String, - port: u16 -} - -impl TcpTransport { - - /// Creates a new TCP transport that connects to the given host. - pub fn new(addr: GenericTransport) -> Self { - if addr.port_num != 0 { - Self { - addr : addr.address, - port : addr.port_num, - } - } else { - Self { - addr : addr.address, - port : 53 - } - } - } -} - - -impl Transport for TcpTransport { - fn send(&self, request: &Request) -> Result { - info!("Opening TCP stream"); - let mut stream = - TcpStream::connect((&*self.addr, self.port))?; - - debug!("Opened"); - - // The message is prepended with the length when sent over TCP, - // so the server knows how long it is (RFC 1035 §4.2.2) - let mut bytes_to_send = request.to_bytes().expect("failed to serialise request"); - Self::prefix_with_length(&mut bytes_to_send); - - info!("Sending {} bytes of data to {:?} over TCP", bytes_to_send.len(), self.addr); - let written_len = stream.write(&bytes_to_send)?; - debug!("Wrote {} bytes", written_len); - - let read_bytes = Self::length_prefixed_read(&mut stream)?; - let response = Response::from_bytes(&read_bytes)?; - Ok(response) - } -} - -impl TcpTransport { - - /// Mutate the given byte buffer, prefixing it with its own length as a - /// big-endian `u16`. - pub(crate) fn prefix_with_length(bytes: &mut Vec) { - let len_bytes = u16::try_from(bytes.len()) - .expect("request too long") - .to_be_bytes(); - - bytes.insert(0, len_bytes[0]); - bytes.insert(1, len_bytes[1]); - } - - /// Reads from the given I/O source as many times as necessary to read a - /// length-prefixed stream of bytes. The first two bytes are taken as a - /// big-endian `u16` to determine the length. Then, that many bytes are - /// read from the source. - /// - /// # Errors - /// - /// Returns an error if there’s a network error during reading, or not - /// enough bytes have been sent. - pub(crate) fn length_prefixed_read(stream: &mut impl Read) -> Result, Error> { - info!("Waiting to receive..."); - - let mut buf = [0; 4096]; - let mut read_len = stream.read(&mut buf[..])?; - - if read_len == 0 { - warn!("Received no bytes!"); - return Err(Error::TruncatedResponse); - } - else if read_len == 1 { - info!("Received one byte of data"); - let second_read_len = stream.read(&mut buf[1..])?; - if second_read_len == 0 { - warn!("Received no bytes the second time!"); - return Err(Error::TruncatedResponse); - } - - read_len += second_read_len; - } - else { - info!("Received {} bytes of data", read_len); - } - - let total_len = u16::from_be_bytes([buf[0], buf[1]]); - if read_len - 2 == usize::from(total_len) { - debug!("We have enough bytes"); - return Ok(buf[2..read_len].to_vec()); - } - - debug!("We need to read {} bytes total", total_len); - let mut combined_buffer = buf[2..read_len].to_vec(); - while combined_buffer.len() < usize::from(total_len) { - let mut extend_buf = [0; 4096]; - let extend_len = stream.read(&mut extend_buf[..])?; - info!("Received further {} bytes of data (of {})", extend_len, total_len); - - if read_len == 0 { - warn!("Read zero bytes!"); - return Err(Error::TruncatedResponse); - } - - combined_buffer.extend(&extend_buf[0 .. extend_len]); - } - - Ok(combined_buffer) - } -} diff --git a/dns-transport/src/tls.rs b/dns-transport/src/tls.rs deleted file mode 100644 index 632eaa7..0000000 --- a/dns-transport/src/tls.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![cfg_attr(not(feature = "tls"), allow(unused))] - -use std::net::TcpStream; -use std::io::Write; - -use log::*; - -use doge_dns::{Request, Response}; -use crate::GenericTransport; - -use super::{Transport, Error, TcpTransport}; -use super::tls_stream::TlsStream; - - -/// The **TLS transport**, which sends DNS wire data using TCP through an -/// encrypted TLS connection. -pub struct TlsTransport { - addr: String, - port: u16 -} - -impl TlsTransport { - - /// Creates a new TLS transport that connects to the given host. - pub fn new(addr: GenericTransport) -> Self { - if addr.port_num != 0 { - Self { - addr : addr.address, - port : addr.port_num, - } - } else { - Self { - addr : addr.address, - port : 853 - } - } - } -} - - - -impl Transport for TlsTransport { - - #[cfg(feature = "with_tls")] - fn send(&self, request: &Request) -> Result { - use native_tls::TlsStream; - - info!("Opening TLS socket"); - - let domain = self.sni_domain(); - info!("Connecting using domain {:?}", domain); - // comminicate that the port must EXPLICATLY BE SEPERATE - let mut stream: TlsStream = Self::stream(&self.addr, self.port)?; - - debug!("Connected"); - - // The message is prepended with the length when sent over TCP, - // so the server knows how long it is (RFC 1035 §4.2.2) - let mut bytes_to_send = request.to_bytes().expect("failed to serialise request"); - TcpTransport::prefix_with_length(&mut bytes_to_send); - - info!("Sending {} bytes of data to {} over TLS", bytes_to_send.len(), self.addr); - stream.write_all(&bytes_to_send)?; - debug!("Wrote all bytes"); - - let read_bytes = TcpTransport::length_prefixed_read(&mut stream)?; - let response = Response::from_bytes(&read_bytes)?; - Ok(response) - } - - #[cfg(not(feature = "with_tls"))] - fn send(&self, request: &Request) -> Result { - unreachable!("TLS feature disabled") - } -} - -impl TlsTransport { - fn sni_domain(&self) -> &str { - if let Some(colon_index) = self.addr.find(':') { - &self.addr[.. colon_index] - } - else { - &self.addr[..] - } - } -} diff --git a/dns-transport/src/tls_stream.rs b/dns-transport/src/tls_stream.rs deleted file mode 100644 index be6443a..0000000 --- a/dns-transport/src/tls_stream.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::net::TcpStream; -use super::Error; -use super::HttpsTransport; -use super::TlsTransport; - -#[cfg(any(feature = "with_nativetls", feature = "with_nativetls_vendored"))] -fn stream_nativetls(domain: &str, port: u16) -> Result, Error> { - let connector = native_tls::TlsConnector::new()?; - let stream = TcpStream::connect((domain, port))?; - Ok(connector.connect(domain, stream)?) -} - -#[cfg(feature = "with_rustls")] -fn stream_rustls(domain: &str, port: u16) -> Result, Error> { - use std::sync::Arc; - - let mut config = rustls::ClientConfig::new(); - - config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - - let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain)?; - - let conn = rustls::ClientSession::new(&Arc::new(config), dns_name); - - let sock = TcpStream::connect((domain, port))?; - let tls = rustls::StreamOwned::new(conn, sock); - - Ok(tls) -} - -pub trait TlsStream { - fn stream(domain: &str, port: u16) -> Result; -} - -#[cfg(any(feature = "with_tls", feature = "with_https"))] -cfg_if::cfg_if! { - if #[cfg(any(feature = "with_nativetls", feature = "with_nativetls_vendored"))] { - - impl TlsStream> for HttpsTransport { - fn stream(domain: &str, port: u16) -> Result, Error> { - stream_nativetls(domain, port) - } - } - - impl TlsStream> for TlsTransport { - fn stream(domain: &str, port: u16) -> Result, Error> { - stream_nativetls(domain, port) - } - } - - } else if #[cfg(feature = "with_rustls")] { - - impl TlsStream> for HttpsTransport { - fn stream(domain: &str, port: u16) -> Result, Error> { - stream_rustls(domain, port) - } - } - - impl TlsStream> for TlsTransport { - fn stream(domain: &str, port: u16) -> Result, Error> { - stream_rustls(domain, port) - } - } - - } else { - unreachable!("tls/https enabled but no tls implementation provided") - } -} - diff --git a/dns-transport/src/udp.rs b/dns-transport/src/udp.rs deleted file mode 100644 index c415133..0000000 --- a/dns-transport/src/udp.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::net::{Ipv4Addr, UdpSocket}; - -use log::*; - -use doge_dns::{Request, Response}; -use crate::GenericTransport; - -use super::{Transport, Error}; - - -/// The **UDP transport**, which sends DNS wire data inside a UDP datagram. -/// -/// # References -/// -/// - [RFC 1035 §4.2.1](https://tools.ietf.org/html/rfc1035) — Domain Names, -/// Implementation and Specification (November 1987) -pub struct UdpTransport { - addr: String, - port: u16, -} - -impl UdpTransport { - - /// Creates a new UDP transport that connects to the given host. - pub fn new(addr: GenericTransport) -> Self { - if addr.port_num != 0 { - Self { - addr : addr.address, - port : addr.port_num, - } - } else { - Self { - addr : addr.address, - port : 53 - } - } - } -} - - -impl Transport for UdpTransport { - fn send(&self, request: &Request) -> Result { - info!("Opening UDP socket"); - // TODO: This will need to be changed for IPv6 support. - let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?; - socket.connect( (&*self.addr, self.port))?; - - debug!("Opened"); - - let bytes_to_send = request.to_bytes().expect("failed to serialise request"); - - info!("Sending {} bytes of data to {} over UDP", bytes_to_send.len(), self.addr); - let written_len = socket.send(&bytes_to_send)?; - debug!("Wrote {} bytes", written_len); - - info!("Waiting to receive..."); - let mut buf = vec![0; 4096]; - let received_len = socket.recv(&mut buf)?; - - info!("Received {} bytes of data", received_len); - let response = Response::from_bytes(&buf[.. received_len])?; - Ok(response) - } -} diff --git a/src/connect.rs b/src/connect.rs index 04af46f..f02724a 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -1,6 +1,6 @@ //! Creating DNS transports based on the user’s input arguments. -use dns_transport::*; +use doge_transport::*; use log::debug; diff --git a/src/output.rs b/src/output.rs index 8d74bf1..5e65962 100644 --- a/src/output.rs +++ b/src/output.rs @@ -6,7 +6,7 @@ use std::env; use doge_dns::{Response, Query, Answer, QClass, ErrorCode, WireError, MandatedLength}; use doge_dns::record::{Record, RecordType, UnknownQtype, OPT}; -use dns_transport::Error as TransportError; +use doge_transport::Error as TransportError; use json::{object, JsonValue}; use crate::colours::Colours; diff --git a/src/requests.rs b/src/requests.rs index 2a0f8ed..4c75b0a 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -1,5 +1,5 @@ //! Request generation based on the user’s input arguments. -use dns_transport::GenericTransport; +use doge_transport::GenericTransport; use crate::connect::{PortNumber, TransportType}; use crate::resolve::{ResolverType, ResolverLookupError}; @@ -84,7 +84,7 @@ pub enum UseEDNS { /// The entry type for `RequestGenerator`: a transport to send a request, and /// a list of one or more DNS queries to send over it, as determined by the /// search path in the resolver. -pub type RequestSet = (Box, Vec); +pub type RequestSet = (Box, Vec); impl RequestGenerator { From c4dc1532a5728999e8a0f3c2ccd2aead98f73614 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 18:23:46 -0600 Subject: [PATCH 07/10] Editing the README for greatness --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ea0616d..4b73ded 100644 --- a/README.md +++ b/README.md @@ -70,13 +70,10 @@ It has colourful output, understands normal command-line argument syntax, suppor ## Installation Currently: - 1. Pull the repo and cd into it - 2. Ensure you have rust installed and openssl-dev ( On Debian: libssl-dev, On Arch: openssl, and RHEL: openssl-devel) - 3. build with cargo and enjoy. - + To install dog, you can download a pre-compiled binary, or you can compile it from source. You _**may**_ be able to install dog using your OS’s package manager, depending on your platform. ### Project existence - I am not a rust expert at all, Honestly I'm the opposite, just learning codding. I use [dog]() on my arch system and a few random *nix + I am not a rust expert at all, Honestly I'm the opposite, just learning codding. I used [`dog`](https://github.com/ogham/dog) on my arch system and a few random *nix Laptops that I perpetually fix and break . As such part of this progect will be outside of my skill set or ability to work on currently. Things such as : *Windows support as a whole *MACOS support as a whole (I'm never buying a mac so this won't be touched by me) I'll merge PRs if thats you thing @@ -86,7 +83,9 @@ Currently: ### Packages - Work in progress + This is currelty a Work in progress + + The current default install is `**cargo install dns-doge**`, dns-doge is one of the internal libs ('I didn't think throught the names for publishing') @@ -94,7 +93,7 @@ Currently: ### Downloads -Binary downloads of dog are available from [the releases section on GitHub](https://github.com/Dj-Codeman/doge/releases/) for ~~64-bit Windows~~, ~~macOS~~, and Linux targets. They contain the compiled executable, the manual page, and shell completions. +Binary downloads of doge are available from [the releases section on GitHub](https://github.com/Dj-Codeman/doge/releases/) for 64-bit Windows, macOS, and Linux targets. They contain the compiled executable, the manual page, and shell completions. ### Compilation From dc7bb3b2c6a65b2890536691ce77716703c1fb68 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 18:56:28 -0600 Subject: [PATCH 08/10] Directions unclear migrated to makefile --- Cargo.lock | 4 +- Justfile | 176 ---------------------------------------------------- README.md | 3 +- makefile | 79 +++++++++++++++++++++++ src/main.rs | 4 +- 5 files changed, 85 insertions(+), 181 deletions(-) delete mode 100644 Justfile create mode 100644 makefile diff --git a/Cargo.lock b/Cargo.lock index 1d2d643..c92336f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -348,9 +348,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.100" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", diff --git a/Justfile b/Justfile deleted file mode 100644 index 0646105..0000000 --- a/Justfile +++ /dev/null @@ -1,176 +0,0 @@ -all: build test xtests -all-release: build-release test-release xtests-release -all-quick: build-quick test-quick xtests-quick - -export DOGE_DEBUG := "" - - -#----------# -# building # -#----------# - -# compile the doge binary -@build: - cargo build - -# compile the doge binary (in release mode) -@build-release: - cargo build --release --verbose - strip "${CARGO_TARGET_DIR:-target}/release/doge" - -# produce an HTML chart of compilation timings -@build-time: - cargo +nightly clean - cargo +nightly build -Z timings - -# compile the doge binary (without some features) -@build-quick: - cargo build --no-default-features - -# check that the doge binary can compile -@check: - cargo check - - -#---------------# -# running tests # -#---------------# - -# run unit tests -@test: - cargo test --workspace -- --quiet - -# run unit tests (in release mode) -@test-release: - cargo test --workspace --release --verbose - -# run unit tests (without some features) -@test-quick: - cargo test --workspace --no-default-features -- --quiet - -# run mutation tests -@test-mutation: - cargo +nightly test --package dns --features=dns/with_mutagen -- --quiet - cargo +nightly mutagen --package dns --features=dns/with_mutagen - - -#------------------------# -# running extended tests # -#------------------------# - -# run extended tests -@xtests *args: - specsheet xtests/{options,live,madns}/*.toml -shide {{args}} \ - -O cmd.target.doge="${CARGO_TARGET_DIR:-../../target}/debug/doge" - -# run extended tests (in release mode) -@xtests-release *args: - specsheet xtests/{options,live,madns}/*.toml {{args}} \ - -O cmd.target.doge="${CARGO_TARGET_DIR:-../../target}/release/doge" - -# run extended tests (omitting certain feature tests) -@xtests-quick *args: - specsheet xtests/options/*.toml xtests/live/{basics,tcp}.toml -shide {{args}} \ - -O cmd.target.doge="${CARGO_TARGET_DIR:-../../target}/debug/doge" - -# run extended tests against a local madns instance -@xtests-madns-local *args: - env MADNS_ARGS="@localhost:5301 --tcp" \ - specsheet xtests/madns/*.toml -shide {{args}} \ - -O cmd.target.doge="${CARGO_TARGET_DIR:-../../target}/debug/doge" - -# display the number of extended tests that get run -@count-xtests: - grep -F '[[cmd]]' -R xtests | wc -l - - -#---------# -# fuzzing # -#---------# - -# run fuzzing on the dns crate -@fuzz: - cargo +nightly fuzz --version - cd dns; cargo +nightly fuzz run fuzz_parsing -- -jobs=`nproc` -workers=`nproc` -runs=69105 - -# print out the data that caused crashes during fuzzing as hexadecimal -@fuzz-hex: - for crash in dns/fuzz/artifacts/fuzz_parsing/crash-*; do echo; echo $crash; hexyl $crash; done - -# remove fuzz log files -@fuzz-clean: - rm dns/fuzz/fuzz-*.log - - -#-----------------------# -# code quality and misc # -#-----------------------# - -# lint the code -@clippy: - touch dns/src/lib.rs - cargo clippy - -# generate a code coverage report using tarpaulin via docker -@coverage-docker: - docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin cargo tarpaulin --all --out Html - -# update dependency versions, and check for outdated ones -@update-deps: - cargo update - command -v cargo-outdated >/dev/null || (echo "cargo-outdated not installed" && exit 1) - cargo outdated - -# list unused dependencies -@unused-deps: - command -v cargo-udeps >/dev/null || (echo "cargo-udeps not installed" && exit 1) - cargo +nightly udeps - -# builds doge and runs extended tests with features disabled -@feature-checks *args: - cargo build --no-default-features - specsheet xtests/features/none.toml -shide {{args}} \ - -O cmd.target.doge="${CARGO_TARGET_DIR:-../../target}/debug/doge" - -# print versions of the necessary build tools -@versions: - rustc --version - cargo --version - - -#---------------# -# documentation # -#---------------# - -# render the documentation -@doc: - cargo doc --no-deps --workspace - -# build the man pages -@man: - mkdir -p "${CARGO_TARGET_DIR:-target}/man" - pandoc --standalone -f markdown -t man man/doge.1.md > "${CARGO_TARGET_DIR:-target}/man/doge.1" - -# build and preview the man page -@man-preview: man - man "${CARGO_TARGET_DIR:-target}/man/doge.1" - - -#-----------# -# packaging # -#-----------# - -# create a distributable package -zip desc exe="doge": - #!/usr/bin/env perl - use Archive::Zip; - -e 'target/release/{{ exe }}' || die 'Binary not built!'; - -e 'target/man/doge.1' || die 'Man page not built!'; - my $zip = Archive::Zip->new(); - $zip->addFile('completions/doge.bash'); - $zip->addFile('completions/doge.zsh'); - $zip->addFile('completions/doge.fish'); - $zip->addFile('target/man/doge.1', 'man/doge.1'); - $zip->addFile('target/release/{{ exe }}', 'bin/{{ exe }}'); - $zip->writeToFileNamed('doge-{{ desc }}.zip') == AZ_OK || die 'Zip write error!'; - system 'unzip -l "doge-{{ desc }}".zip' diff --git a/README.md b/README.md index 4b73ded..bbd7464 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,8 @@ Currently: This is currelty a Work in progress - The current default install is `**cargo install dns-doge**`, dns-doge is one of the internal libs ('I didn't think throught the names for publishing') + $ cargo install dns-doge + diff --git a/makefile b/makefile new file mode 100644 index 0000000..ed692e9 --- /dev/null +++ b/makefile @@ -0,0 +1,79 @@ +# Makefile + +# Define phony targets +.PHONY: all all-release all-quick build test build-release test-release build-time build-quick check test-quick clippy coverage-docker update-deps unused-deps versions doc man man-preview + +# Set DOGE_DEBUG to empty string +export DOGE_DEBUG := "" + +# Targets related to building +build: + @cargo build + +build-release: + @cargo build --release --verbose + @strip "${CARGO_TARGET_DIR:-target}/release/doge" + +build-time: + @cargo +nightly clean + @cargo +nightly build -Z timings + +build-quick: + @cargo build --no-default-features + +# Check the compilation +check: + @cargo check + +# Targets related to testing +test: + @cargo test --workspace -- --quiet + +test-release: + @cargo test --workspace --release --verbose + +test-quick: + @cargo test --workspace --no-default-features -- --quiet + +# Targets related to fuzzing +# fuzz: +# @cargo +nightly fuzz --version +# @cd dns; cargo +nightly fuzz run fuzz_parsing -- -jobs=`nproc` -workers=`nproc` -runs=69105 + +# fuzz-hex: +# @for crash in dns/fuzz/artifacts/fuzz_parsing/crash-*; do echo; echo $$crash; hexyl $$crash; done + +# fuzz-clean: +# @rm dns/fuzz/fuzz-*.log + +# Targets related to code quality and miscellaneous +clippy: + @touch dns/src/lib.rs + @cargo clippy + +coverage-docker: + @docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin cargo tarpaulin --all --out Html + +update-deps: + @cargo update + @command -v cargo-outdated >/dev/null || (echo "cargo-outdated not installed" && exit 1) + @cargo outdated + +unused-deps: + @command -v cargo-udeps >/dev/null || (echo "cargo-udeps not installed" && exit 1) + @cargo +nightly udeps + +versions: + @rustc --version + @cargo --version + +# Targets related to documentation +doc: + @cargo doc --no-deps --workspace + +man: + @mkdir -p "${CARGO_TARGET_DIR:-target}/man" + @pandoc --standalone -f markdown -t man man/doge.1.md > "${CARGO_TARGET_DIR:-target}/man/doge.1" + +man-preview: man + @man "${CARGO_TARGET_DIR:-target}/man/doge.1" diff --git a/src/main.rs b/src/main.rs index d869b87..b7e7480 100644 --- a/src/main.rs +++ b/src/main.rs @@ -182,13 +182,13 @@ fn disabled_feature_check(options: &Options) { use crate::connect::TransportType; #[cfg(all(not(feature = "with_tls"), not(feature = "with_rustls_tls")))] - if options.requests.inputs.transport_types.contains(&TransportType::TLS(None)) { + if options.requests.inputs.transport_types.contains(&TransportType::TLS) { eprintln!("doge: Cannot use '--tls': This version of dog has been compiled without TLS support"); exit(exits::OPTIONS_ERROR); } #[cfg(all(not(feature = "with_https"), not(feature = "with_rustls_https")))] - if options.requests.inputs.transport_types.contains(&TransportType::HTTPS(None)) { + if options.requests.inputs.transport_types.contains(&TransportType::HTTPS) { eprintln!("doge: Cannot use '--https': This version of dog has been compiled without HTTPS support"); exit(exits::OPTIONS_ERROR); } From 05af5fd1df27e2f4f61f2b8a307f1c6c71ede430 Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 19:50:01 -0600 Subject: [PATCH 09/10] pre_publish touch ups --- Cargo.toml | 4 ++-- makefile | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aaf1fb4..4526fcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "doge" +name = "dns-doge" description = "A command-line DNS client" authors = ["Darrion Whitfield "] @@ -7,7 +7,7 @@ categories = ["command-line-utilities"] edition = "2021" exclude = [ "/completions/*", "/man/*", "/xtests/*", - "/dog-screenshot.png", "/Justfile", "/README.md", "/.rustfmt.toml", "/.travis.yml", + "/dog-screenshot.png", "/Justfile", "/README.md", "/.rustfmt.toml", "/.travis.yml", /makefile, ] homepage = "https://dns.lookup.dog/" license = "MIT" diff --git a/makefile b/makefile index ed692e9..4db662b 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,7 @@ # Makefile # Define phony targets -.PHONY: all all-release all-quick build test build-release test-release build-time build-quick check test-quick clippy coverage-docker update-deps unused-deps versions doc man man-preview +.PHONY: all all-release all-quick build test build-release install test-release build-time build-quick check test-quick clippy coverage-docker update-deps unused-deps versions doc man man-preview # Set DOGE_DEBUG to empty string export DOGE_DEBUG := "" @@ -12,7 +12,7 @@ build: build-release: @cargo build --release --verbose - @strip "${CARGO_TARGET_DIR:-target}/release/doge" + # @strip "${CARGO_TARGET_DIR:-target}/release/doge" build-time: @cargo +nightly clean @@ -25,6 +25,11 @@ build-quick: check: @cargo check +# Installation of the bin +install: + @mv "./target/release/doge" /usr/bin/doge + @chmod +x /usr/bin/doge + # Targets related to testing test: @cargo test --workspace -- --quiet @@ -36,15 +41,6 @@ test-quick: @cargo test --workspace --no-default-features -- --quiet # Targets related to fuzzing -# fuzz: -# @cargo +nightly fuzz --version -# @cd dns; cargo +nightly fuzz run fuzz_parsing -- -jobs=`nproc` -workers=`nproc` -runs=69105 - -# fuzz-hex: -# @for crash in dns/fuzz/artifacts/fuzz_parsing/crash-*; do echo; echo $$crash; hexyl $$crash; done - -# fuzz-clean: -# @rm dns/fuzz/fuzz-*.log # Targets related to code quality and miscellaneous clippy: From d5119bd01d6087a4e5b7625ed0226bb26d7cedcb Mon Sep 17 00:00:00 2001 From: Darrion Whitfield Date: Wed, 21 Feb 2024 19:51:06 -0600 Subject: [PATCH 10/10] pre_publish touch ups --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4526fcc..5f6e32f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ categories = ["command-line-utilities"] edition = "2021" exclude = [ "/completions/*", "/man/*", "/xtests/*", - "/dog-screenshot.png", "/Justfile", "/README.md", "/.rustfmt.toml", "/.travis.yml", /makefile, + "/dog-screenshot.png", "/Justfile", "/README.md", "/.rustfmt.toml", "/.travis.yml", "/makefile", ] homepage = "https://dns.lookup.dog/" license = "MIT"