From 0f40ab89e3b523ac206077d932a0e2d40d75f7e0 Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Fri, 3 Mar 2023 00:10:21 -0500 Subject: [PATCH] finialize initial dns + caching --- Cargo.lock | 789 ++++++++++++++++++++++++- Cargo.toml | 7 +- packet/Cargo.lock | 7 - packet/Cargo.toml | 6 - packet/src/record.rs | 227 ------- resolver/Cargo.lock | 357 ----------- resolver/Cargo.toml | 9 - resolver/src/config.rs | 23 - resolver/src/lib.rs | 33 -- resolver/src/server.rs | 121 ---- src/config.rs | 35 ++ src/main.rs | 54 +- {packet/src => src/packet}/buffer.rs | 96 ++- {packet/src => src/packet}/header.rs | 5 +- packet/src/lib.rs => src/packet/mod.rs | 62 +- {packet/src => src/packet}/query.rs | 20 +- {packet/src => src/packet}/question.rs | 4 +- src/packet/record.rs | 498 ++++++++++++++++ {packet/src => src/packet}/result.rs | 2 +- src/server/binding.rs | 150 +++++ src/server/mod.rs | 3 + src/server/resolver.rs | 165 ++++++ src/server/server.rs | 73 +++ 23 files changed, 1863 insertions(+), 883 deletions(-) delete mode 100644 packet/Cargo.lock delete mode 100644 packet/Cargo.toml delete mode 100644 packet/src/record.rs delete mode 100644 resolver/Cargo.lock delete mode 100644 resolver/Cargo.toml delete mode 100644 resolver/src/config.rs delete mode 100644 resolver/src/lib.rs delete mode 100644 resolver/src/server.rs create mode 100644 src/config.rs rename {packet/src => src/packet}/buffer.rs (70%) rename {packet/src => src/packet}/header.rs (97%) rename packet/src/lib.rs => src/packet/mod.rs (60%) rename {packet/src => src/packet}/query.rs (60%) rename {packet/src => src/packet}/question.rs (94%) create mode 100644 src/packet/record.rs rename {packet/src => src/packet}/result.rs (99%) create mode 100644 src/server/binding.rs create mode 100644 src/server/mod.rs create mode 100644 src/server/resolver.rs create mode 100644 src/server/server.rs diff --git a/Cargo.lock b/Cargo.lock index 4120eec..9879e30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,35 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "async-io" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +dependencies = [ + "async-lock", + "autocfg", + "concurrent-queue", + "futures-lite", + "libc", + "log", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "windows-sys 0.42.0", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + [[package]] name = "async-recursion" version = "1.0.2" @@ -25,18 +54,228 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "camino" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "concurrent-queue" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hermit-abi" version = "0.2.6" @@ -46,12 +285,58 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -71,12 +356,30 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "mio" version = "0.8.6" @@ -85,10 +388,46 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.45.0", ] +[[package]] +name = "moka" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6446f16d504e3d575df79cabb11bfbe9f24b17e9562d964a815db7b28ae3ec" +dependencies = [ + "async-io", + "async-lock", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "futures-util", + "num_cpus", + "once_cell", + "parking_lot", + "quanta", + "rustc_version", + "scheduled-thread-pool", + "skeptic", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -100,8 +439,22 @@ dependencies = [ ] [[package]] -name = "packet" -version = "0.1.0" +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] name = "parking_lot" @@ -132,6 +485,26 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +dependencies = [ + "autocfg", + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "windows-sys 0.42.0", +] + [[package]] name = "proc-macro2" version = "1.0.51" @@ -141,6 +514,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quanta" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e31331286705f455e56cca62e0e717158474ff02b7936c1fa596d983f4ae27" +dependencies = [ + "crossbeam-utils", + "libc", + "mach", + "once_cell", + "raw-cpuid", + "wasi 0.10.2+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quote" version = "1.0.23" @@ -150,6 +550,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -160,12 +569,50 @@ dependencies = [ ] [[package]] -name = "resolver" -version = "0.1.0" +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "async-recursion", - "packet", - "tokio", + "semver", +] + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" +dependencies = [ + "parking_lot", ] [[package]] @@ -174,6 +621,55 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -183,6 +679,30 @@ dependencies = [ "libc", ] +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -210,6 +730,55 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tokio" version = "1.25.0" @@ -241,18 +810,208 @@ dependencies = [ "syn", ] +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "triomphe" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -269,6 +1028,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -360,6 +1128,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" name = "wrapper" version = "0.1.0" dependencies = [ - "resolver", + "async-recursion", + "moka", "tokio", + "tracing", + "tracing-subscriber", ] diff --git a/Cargo.toml b/Cargo.toml index 1d0690c..a8c6cd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -resolver = { path = "resolver"} -tokio = { version = "1", features = ["full"] } \ No newline at end of file +tokio = { version = "1", features = ["full"] } +async-recursion = "1" +tracing = "0.1.37" +tracing-subscriber = "0.3.16" +moka = { version = "0.10.0", features = ["future"] } \ No newline at end of file diff --git a/packet/Cargo.lock b/packet/Cargo.lock deleted file mode 100644 index 3f0df66..0000000 --- a/packet/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "packet" -version = "0.1.0" diff --git a/packet/Cargo.toml b/packet/Cargo.toml deleted file mode 100644 index 2e3797f..0000000 --- a/packet/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "packet" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/packet/src/record.rs b/packet/src/record.rs deleted file mode 100644 index c7ff1ac..0000000 --- a/packet/src/record.rs +++ /dev/null @@ -1,227 +0,0 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; - -use super::{query::QueryType, buffer::PacketBuffer, Result}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[allow(dead_code)] -pub enum DnsRecord { - UNKNOWN { - domain: String, - qtype: u16, - data_len: u16, - ttl: u32, - }, // 0 - A { - domain: String, - addr: Ipv4Addr, - ttl: u32, - }, // 1 - NS { - domain: String, - host: String, - ttl: u32, - }, // 2 - CNAME { - domain: String, - host: String, - ttl: u32, - }, // 5 - MX { - domain: String, - priority: u16, - host: String, - ttl: u32, - }, // 15 - AAAA { - domain: String, - addr: Ipv6Addr, - ttl: u32, - }, // 28 -} - -impl DnsRecord { - pub fn read(buffer: &mut PacketBuffer) -> Result { - let mut domain = String::new(); - buffer.read_qname(&mut domain)?; - - let qtype_num = buffer.read_u16()?; - let qtype = QueryType::from_num(qtype_num); - let _ = buffer.read_u16()?; - let ttl = buffer.read_u32()?; - let data_len = buffer.read_u16()?; - - match qtype { - QueryType::A => { - let raw_addr = buffer.read_u32()?; - let addr = Ipv4Addr::new( - ((raw_addr >> 24) & 0xFF) as u8, - ((raw_addr >> 16) & 0xFF) as u8, - ((raw_addr >> 8) & 0xFF) as u8, - (raw_addr & 0xFF) as u8, - ); - - Ok(Self::A { domain, addr, ttl }) - } - QueryType::AAAA => { - let raw_addr1 = buffer.read_u32()?; - let raw_addr2 = buffer.read_u32()?; - let raw_addr3 = buffer.read_u32()?; - let raw_addr4 = buffer.read_u32()?; - let addr = Ipv6Addr::new( - ((raw_addr1 >> 16) & 0xFFFF) as u16, - (raw_addr1 & 0xFFFF) as u16, - ((raw_addr2 >> 16) & 0xFFFF) as u16, - (raw_addr2 & 0xFFFF) as u16, - ((raw_addr3 >> 16) & 0xFFFF) as u16, - (raw_addr3 & 0xFFFF) as u16, - ((raw_addr4 >> 16) & 0xFFFF) as u16, - (raw_addr4 & 0xFFFF) as u16, - ); - - Ok(Self::AAAA { domain, addr, ttl }) - } - QueryType::NS => { - let mut ns = String::new(); - buffer.read_qname(&mut ns)?; - - Ok(Self::NS { - domain, - host: ns, - ttl, - }) - } - QueryType::CNAME => { - let mut cname = String::new(); - buffer.read_qname(&mut cname)?; - - Ok(Self::CNAME { - domain, - host: cname, - ttl, - }) - } - QueryType::MX => { - let priority = buffer.read_u16()?; - let mut mx = String::new(); - buffer.read_qname(&mut mx)?; - - Ok(Self::MX { - domain, - priority, - host: mx, - ttl, - }) - } - QueryType::UNKNOWN(_) => { - buffer.step(data_len as usize)?; - - Ok(Self::UNKNOWN { - domain, - qtype: qtype_num, - data_len, - ttl, - }) - } - } - } - - pub fn write(&self, buffer: &mut PacketBuffer) -> Result { - let start_pos = buffer.pos(); - - match *self { - Self::A { - ref domain, - ref addr, - ttl, - } => { - buffer.write_qname(domain)?; - buffer.write_u16(QueryType::A.to_num())?; - buffer.write_u16(1)?; - buffer.write_u32(ttl)?; - buffer.write_u16(4)?; - - let octets = addr.octets(); - buffer.write_u8(octets[0])?; - buffer.write_u8(octets[1])?; - buffer.write_u8(octets[2])?; - buffer.write_u8(octets[3])?; - } - Self::NS { - ref domain, - ref host, - ttl, - } => { - buffer.write_qname(domain)?; - buffer.write_u16(QueryType::NS.to_num())?; - buffer.write_u16(1)?; - buffer.write_u32(ttl)?; - - let pos = buffer.pos(); - buffer.write_u16(0)?; - - buffer.write_qname(host)?; - - let size = buffer.pos() - (pos + 2); - buffer.set_u16(pos, size as u16)?; - } - Self::CNAME { - ref domain, - ref host, - ttl, - } => { - buffer.write_qname(domain)?; - buffer.write_u16(QueryType::CNAME.to_num())?; - buffer.write_u16(1)?; - buffer.write_u32(ttl)?; - - let pos = buffer.pos(); - buffer.write_u16(0)?; - - buffer.write_qname(host)?; - - let size = buffer.pos() - (pos + 2); - buffer.set_u16(pos, size as u16)?; - } - Self::MX { - ref domain, - priority, - ref host, - ttl, - } => { - buffer.write_qname(domain)?; - buffer.write_u16(QueryType::MX.to_num())?; - buffer.write_u16(1)?; - buffer.write_u32(ttl)?; - - let pos = buffer.pos(); - buffer.write_u16(0)?; - - buffer.write_u16(priority)?; - buffer.write_qname(host)?; - - let size = buffer.pos() - (pos + 2); - buffer.set_u16(pos, size as u16)?; - } - Self::AAAA { - ref domain, - ref addr, - ttl, - } => { - buffer.write_qname(domain)?; - buffer.write_u16(QueryType::AAAA.to_num())?; - buffer.write_u16(1)?; - buffer.write_u32(ttl)?; - buffer.write_u16(16)?; - - for octet in &addr.segments() { - buffer.write_u16(*octet)?; - } - } - Self::UNKNOWN { .. } => { - println!("Skipping record: {self:?}"); - } - } - - Ok(buffer.pos() - start_pos) - } -} \ No newline at end of file diff --git a/resolver/Cargo.lock b/resolver/Cargo.lock deleted file mode 100644 index 66ac595..0000000 --- a/resolver/Cargo.lock +++ /dev/null @@ -1,357 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "async-recursion" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.45.0", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "packet" -version = "0.1.0" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "proc-macro2" -version = "1.0.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "resolver" -version = "0.1.0" -dependencies = [ - "async-recursion", - "packet", - "tokio", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tokio" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.42.0", -] - -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -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", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -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", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/resolver/Cargo.toml b/resolver/Cargo.toml deleted file mode 100644 index bc56d62..0000000 --- a/resolver/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "resolver" -version = "0.1.0" -edition = "2021" - -[dependencies] -packet = { path = "../packet" } -tokio = { version = "1", features = ["full"] } -async-recursion = "1" \ No newline at end of file diff --git a/resolver/src/config.rs b/resolver/src/config.rs deleted file mode 100644 index ef7d5a8..0000000 --- a/resolver/src/config.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::net::IpAddr; - -#[derive(Clone)] -pub struct Config { - fallback: IpAddr -} - -impl Config { - - pub fn new() -> Self { - let fallback = "9.9.9.9".parse::().expect("Failed to create default ns fallback"); - Self { fallback } - } - - pub fn get_fallback_ns(&self) -> &IpAddr { - &self.fallback - } - - pub fn set_fallback_ns(&mut self, addr: &IpAddr) { - self.fallback = *addr; - } - -} \ No newline at end of file diff --git a/resolver/src/lib.rs b/resolver/src/lib.rs deleted file mode 100644 index 98df317..0000000 --- a/resolver/src/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::net::SocketAddr; -use server::handle_query; -use tokio::net::UdpSocket; -use packet::Result; - -mod server; -mod config; - -pub use config::Config as Config; - -pub struct DnsResolver { - config: Config -} - -impl DnsResolver { - - pub fn new(config: Config) -> Self { - Self { config } - } - - pub async fn bind(self, addr: SocketAddr) -> Result<()> { - - let socket = UdpSocket::bind(addr).await?; - - loop { - match handle_query(&socket, &self.config).await { - Ok(_) => {} - Err(e) => eprintln!("An error occurred: {e}"), - } - } - } - -} \ No newline at end of file diff --git a/resolver/src/server.rs b/resolver/src/server.rs deleted file mode 100644 index 764b7a2..0000000 --- a/resolver/src/server.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::net::IpAddr; -use async_recursion::async_recursion; -use packet::{PacketType, Packet, Result, PacketQuestion, PacketBuffer, ResultCode}; -use tokio::net::UdpSocket; - -use crate::config::Config; - - -async fn lookup(qname: &str, qtype: PacketType, server: (IpAddr, u16)) -> Result { - let socket = UdpSocket::bind("0.0.0.0:43210").await?; - - let mut packet = Packet::new(); - - packet.header.id = 6666; - packet.header.questions = 1; - packet.header.recursion_desired = true; - packet - .questions - .push(PacketQuestion::new(qname.to_string(), qtype)); - - let mut req_buffer = PacketBuffer::new(); - packet.write(&mut req_buffer)?; - socket.send_to(&req_buffer.buf[0..req_buffer.pos], server).await?; - - let mut res_buffer = PacketBuffer::new(); - socket.recv_from(&mut res_buffer.buf).await?; - - Packet::from_buffer(&mut res_buffer) -} - -#[async_recursion] -async fn recursive_lookup(qname: &str, qtype: PacketType, config: &Config) -> Result { - - let mut ns = *config.get_fallback_ns(); - - loop { - println!("attempting lookup of {qtype:?} {qname} with ns {ns}"); - - let ns_copy = ns; - - let server = (ns_copy, 53); - let response = lookup(qname, qtype, server).await?; - - if !response.answers.is_empty() && response.header.rescode == ResultCode::NOERROR { - return Ok(response); - } - - if response.header.rescode == ResultCode::NXDOMAIN { - return Ok(response); - } - - if let Some(new_ns) = response.get_resolved_ns(qname) { - ns = new_ns; - - continue; - } - - let new_ns_name = match response.get_unresolved_ns(qname) { - Some(x) => x, - None => return Ok(response), - }; - - let recursive_response = recursive_lookup(new_ns_name, PacketType::A, config).await?; - - if let Some(new_ns) = recursive_response.get_random_a() { - ns = new_ns; - } else { - return Ok(response); - } - } -} - -pub async fn handle_query(socket: &UdpSocket, config: &Config) -> Result<()> { - let mut req_buffer = PacketBuffer::new(); - - let (_, src) = socket.recv_from(&mut req_buffer.buf).await?; - - let mut request = Packet::from_buffer(&mut req_buffer)?; - - let mut packet = Packet::new(); - packet.header.id = request.header.id; - packet.header.recursion_desired = true; - packet.header.recursion_available = true; - packet.header.response = true; - - if let Some(question) = request.questions.pop() { - println!("Received query: {question:?}"); - - if let Ok(result) = recursive_lookup(&question.name, question.qtype, config).await { - packet.questions.push(question.clone()); - packet.header.rescode = result.header.rescode; - - for rec in result.answers { - println!("Answer: {rec:?}"); - packet.answers.push(rec); - } - for rec in result.authorities { - println!("Authority: {rec:?}"); - packet.authorities.push(rec); - } - for rec in result.resources { - println!("Resource: {rec:?}"); - packet.resources.push(rec); - } - } else { - packet.header.rescode = ResultCode::SERVFAIL; - } - } else { - packet.header.rescode = ResultCode::FORMERR; - } - - let mut res_buffer = PacketBuffer::new(); - packet.write(&mut res_buffer)?; - - let len = res_buffer.pos(); - let data = res_buffer.get_range(0, len)?; - - socket.send_to(data, src).await?; - - Ok(()) -} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9350adf --- /dev/null +++ b/src/config.rs @@ -0,0 +1,35 @@ +use std::net::IpAddr; + +#[derive(Clone)] +pub struct Config { + fallback: IpAddr, + port: u16, +} + +impl Config { + pub fn new() -> Self { + let fallback = "9.9.9.9" + .parse::() + .expect("Failed to create default ns fallback"); + Self { + fallback, + port: 2000, + } + } + + pub fn get_fallback_ns(&self) -> &IpAddr { + &self.fallback + } + + pub fn get_port(&self) -> u16 { + self.port + } + + pub fn set_fallback_ns(&mut self, addr: &IpAddr) { + self.fallback = *addr; + } + + pub fn set_port(&mut self, port: u16) { + self.port = port; + } +} diff --git a/src/main.rs b/src/main.rs index 0b767de..c891d50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,48 @@ -use std::net::SocketAddr; +use std::{time::{UNIX_EPOCH, SystemTime}, env, net::IpAddr}; -use resolver::{DnsResolver, Config}; +use config::Config; + +use server::server::Server; +use tracing::metadata::LevelFilter; +use tracing_subscriber::{ + filter::filter_fn, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, +}; + +mod config; +mod packet; +mod server; #[tokio::main] -async fn main () { - - let config = Config::new(); +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_filter(LevelFilter::TRACE) + .with_filter(filter_fn(|metadata| { + metadata.target().starts_with("wrapper") + })), + ) + .init(); - let resolver = DnsResolver::new(config); + let mut config = Config::new(); - let addr = "[::]:2000".parse::() - .expect("Failed to create binding"); - - resolver.bind(addr).await - .expect("Failed to start dns server"); + if let Ok(port) = env::var("PORT").unwrap_or(String::new()).parse::() { + config.set_port(port); + } -} \ No newline at end of file + if let Ok(fallback) = env::var("FALLBACK_DNS").unwrap_or(String::new()).parse::() { + config.set_fallback_ns(&fallback); + } + + let server = Server::new(config).await.expect("Failed to bind server"); + + server.run().await.unwrap(); +} + +pub fn get_time() -> u64 { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + since_the_epoch.as_millis() as u64 +} diff --git a/packet/src/buffer.rs b/src/packet/buffer.rs similarity index 70% rename from packet/src/buffer.rs rename to src/packet/buffer.rs index 4394705..4ecc605 100644 --- a/packet/src/buffer.rs +++ b/src/packet/buffer.rs @@ -1,15 +1,27 @@ use super::Result; pub struct PacketBuffer { - pub buf: [u8; 512], + pub buf: Vec, pub pos: usize, + pub size: usize, } impl PacketBuffer { - pub fn new() -> Self { + pub fn new(buf: Vec) -> Self { Self { - buf: [0; 512], + buf, pos: 0, + size: 0, + } + } + + fn check(&mut self, pos: usize) { + if self.size < pos { + self.size = pos; + } + + if self.buf.len() <= self.size { + self.buf.resize(self.size + 1, 0x00); } } @@ -30,9 +42,11 @@ impl PacketBuffer { } pub fn read(&mut self) -> Result { - if self.pos >= 512 { - return Err("End of buffer".into()); - } + // if self.pos >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(self.pos); let res = self.buf[self.pos]; self.pos += 1; @@ -40,16 +54,20 @@ impl PacketBuffer { } pub fn get(&mut self, pos: usize) -> Result { - if pos >= 512 { - return Err("End of buffer".into()); - } + // if pos >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(pos); Ok(self.buf[pos]) } pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> { - if start + len >= 512 { - return Err("End of buffer".into()); - } + // if start + len >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(start + len); Ok(&self.buf[start..start + len]) } @@ -85,13 +103,7 @@ impl PacketBuffer { let len = self.get(pos)?; - // A two byte sequence, where the two highest bits of the first byte is - // set, represents a offset relative to the start of the buffer. We - // handle this by jumping to the offset, setting a flag to indicate - // that we shouldn't update the shared buffer position once done. if (len & 0xC0) == 0xC0 { - // When a jump is performed, we only modify the shared buffer - // position once, and avoid making the change later on. if !jumped { self.seek(pos + 2)?; } @@ -106,7 +118,6 @@ impl PacketBuffer { pos += 1; - // Names are terminated by an empty label of length 0 if len == 0 { break; } @@ -128,10 +139,38 @@ impl PacketBuffer { Ok(()) } - pub fn write(&mut self, val: u8) -> Result<()> { - if self.pos >= 512 { - return Err("End of buffer".into()); + pub fn read_string(&mut self, outstr: &mut String) -> Result<()> { + let len = self.read()?; + + self.read_string_n(outstr, len)?; + + Ok(()) + } + + pub fn read_string_n(&mut self, outstr: &mut String, len: u8) -> Result<()> { + let mut pos = self.pos; + + let str_buffer = self.get_range(pos, len as usize)?; + + let mut i = 0; + for b in str_buffer { + let c = *b as char; + if c == '\0' { + break; + } + outstr.push(c); + i += 1; } + + pos += i; + self.seek(pos)?; + + Ok(()) + } + + pub fn write(&mut self, val: u8) -> Result<()> { + self.check(self.pos); + self.buf[self.pos] = val; self.pos += 1; Ok(()) @@ -162,9 +201,6 @@ impl PacketBuffer { pub fn write_qname(&mut self, qname: &str) -> Result<()> { for label in qname.split('.') { let len = label.len(); - if len > 0x34 { - return Err("Single label exceeds 63 characters of length".into()); - } self.write_u8(len as u8)?; for b in label.as_bytes() { @@ -177,6 +213,14 @@ impl PacketBuffer { Ok(()) } + pub fn write_string(&mut self, text: &str) -> Result<()> { + for b in text.as_bytes() { + self.write_u8(*b)?; + } + + Ok(()) + } + pub fn set(&mut self, pos: usize, val: u8) -> Result<()> { self.buf[pos] = val; @@ -189,4 +233,4 @@ impl PacketBuffer { Ok(()) } -} \ No newline at end of file +} diff --git a/packet/src/header.rs b/src/packet/header.rs similarity index 97% rename from packet/src/header.rs rename to src/packet/header.rs index b2bf1a1..a75f6ba 100644 --- a/packet/src/header.rs +++ b/src/packet/header.rs @@ -1,4 +1,4 @@ -use super::{buffer::PacketBuffer, Result, result::ResultCode}; +use super::{buffer::PacketBuffer, result::ResultCode, Result}; #[derive(Clone, Debug)] pub struct DnsHeader { @@ -48,7 +48,6 @@ impl DnsHeader { pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> { self.id = buffer.read_u16()?; - let flags = buffer.read_u16()?; let a = (flags >> 8) as u8; let b = (flags & 0xFF) as u8; @@ -99,4 +98,4 @@ impl DnsHeader { Ok(()) } -} \ No newline at end of file +} diff --git a/packet/src/lib.rs b/src/packet/mod.rs similarity index 60% rename from packet/src/lib.rs rename to src/packet/mod.rs index c7a8eb9..0b7cb7b 100644 --- a/packet/src/lib.rs +++ b/src/packet/mod.rs @@ -1,16 +1,19 @@ use std::net::IpAddr; -use self::{header::DnsHeader, question::DnsQuestion, record::DnsRecord, query::QueryType}; +use self::{ + buffer::PacketBuffer, header::DnsHeader, query::QueryType, question::DnsQuestion, + record::DnsRecord, +}; type Error = Box; pub type Result = std::result::Result; -mod buffer; -mod header; -mod query; -mod question; -mod record; -mod result; +pub mod buffer; +pub mod header; +pub mod query; +pub mod question; +pub mod record; +pub mod result; #[derive(Clone, Debug)] pub struct Packet { @@ -21,12 +24,6 @@ pub struct Packet { pub resources: Vec, } -pub use buffer::PacketBuffer; -pub use result::ResultCode; - -pub use query::QueryType as PacketType; -pub use question::DnsQuestion as PacketQuestion; - impl Packet { pub fn new() -> Self { Self { @@ -88,9 +85,6 @@ impl Packet { Ok(()) } - /// It's useful to be able to pick a random A record from a packet. When we - /// get multiple IP's for a single name, it doesn't matter which one we - /// choose, so in those cases we can now pick one at random. pub fn get_random_a(&self) -> Option { self.answers .iter() @@ -102,55 +96,35 @@ impl Packet { .next() } - /// A helper function which returns an iterator over all name servers in - /// the authorities section, represented as (domain, host) tuples fn get_ns<'a>(&'a self, qname: &'a str) -> impl Iterator { self.authorities .iter() - // In practice, these are always NS records in well formed packages. - // Convert the NS records to a tuple which has only the data we need - // to make it easy to work with. .filter_map(|record| match record { DnsRecord::NS { domain, host, .. } => Some((domain.as_str(), host.as_str())), _ => None, }) - // Discard servers which aren't authoritative to our query .filter(move |(domain, _)| qname.ends_with(*domain)) } - /// We'll use the fact that name servers often bundle the corresponding - /// A records when replying to an NS query to implement a function that - /// returns the actual IP for an NS record if possible. pub fn get_resolved_ns(&self, qname: &str) -> Option { - // Get an iterator over the nameservers in the authorities section self.get_ns(qname) - // Now we need to look for a matching A record in the additional - // section. Since we just want the first valid record, we can just - // build a stream of matching records. .flat_map(|(_, host)| { self.resources .iter() - // Filter for A records where the domain match the host - // of the NS record that we are currently processing .filter_map(move |record| match record { - DnsRecord::A { domain, addr, .. } if domain == host => Some(IpAddr::V4(*addr)), - DnsRecord::AAAA { domain, addr, .. } if domain == host => Some(IpAddr::V6(*addr)), + DnsRecord::A { domain, addr, .. } if domain == host => { + Some(IpAddr::V4(*addr)) + } + DnsRecord::AAAA { domain, addr, .. } if domain == host => { + Some(IpAddr::V6(*addr)) + } _ => None, }) }) - // Finally, pick the first valid entry .next() } - /// However, not all name servers are as that nice. In certain cases there won't - /// be any A records in the additional section, and we'll have to perform *another* - /// lookup in the midst. For this, we introduce a method for returning the host - /// name of an appropriate name server. pub fn get_unresolved_ns<'a>(&'a self, qname: &'a str) -> Option<&'a str> { - // Get an iterator over the nameservers in the authorities section - self.get_ns(qname) - .map(|(_, host)| host) - // Finally, pick the first valid entry - .next() + self.get_ns(qname).map(|(_, host)| host).next() } -} \ No newline at end of file +} diff --git a/packet/src/query.rs b/src/packet/query.rs similarity index 60% rename from packet/src/query.rs rename to src/packet/query.rs index 8804d15..cae6f09 100644 --- a/packet/src/query.rs +++ b/src/packet/query.rs @@ -4,8 +4,14 @@ pub enum QueryType { A, // 1 NS, // 2 CNAME, // 5 + SOA, // 6 + PTR, // 12 MX, // 15 + TXT, // 16 AAAA, // 28 + SRV, // 33 + OPT, // 41 + CAA, // 257 } impl QueryType { @@ -15,8 +21,14 @@ impl QueryType { Self::A => 1, Self::NS => 2, Self::CNAME => 5, + Self::SOA => 6, + Self::PTR => 12, Self::MX => 15, + Self::TXT => 16, Self::AAAA => 28, + Self::SRV => 33, + Self::OPT => 41, + Self::CAA => 257, } } @@ -25,9 +37,15 @@ impl QueryType { 1 => Self::A, 2 => Self::NS, 5 => Self::CNAME, + 6 => Self::SOA, + 12 => Self::PTR, 15 => Self::MX, + 16 => Self::TXT, 28 => Self::AAAA, + 33 => Self::SRV, + 41 => Self::OPT, + 257 => Self::CAA, _ => Self::UNKNOWN(num), } } -} \ No newline at end of file +} diff --git a/packet/src/question.rs b/src/packet/question.rs similarity index 94% rename from packet/src/question.rs rename to src/packet/question.rs index 076de00..9042e1c 100644 --- a/packet/src/question.rs +++ b/src/packet/question.rs @@ -1,6 +1,6 @@ use super::{buffer::PacketBuffer, query::QueryType, Result}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DnsQuestion { pub name: String, pub qtype: QueryType, @@ -28,4 +28,4 @@ impl DnsQuestion { Ok(()) } -} \ No newline at end of file +} diff --git a/src/packet/record.rs b/src/packet/record.rs new file mode 100644 index 0000000..c29dd8f --- /dev/null +++ b/src/packet/record.rs @@ -0,0 +1,498 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +use tracing::{trace, warn}; + +use super::{buffer::PacketBuffer, query::QueryType, Result}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[allow(dead_code)] +pub enum DnsRecord { + UNKNOWN { + domain: String, + qtype: u16, + data_len: u16, + ttl: u32, + }, // 0 + A { + domain: String, + addr: Ipv4Addr, + ttl: u32, + }, // 1 + NS { + domain: String, + host: String, + ttl: u32, + }, // 2 + CNAME { + domain: String, + host: String, + ttl: u32, + }, // 5 + SOA { + domain: String, + mname: String, + nname: String, + serial: u32, + refresh: u32, + retry: u32, + expire: u32, + minimum: u32, + ttl: u32, + }, // 6 + PTR { + domain: String, + pointer: String, + ttl: u32, + }, // 12 + MX { + domain: String, + priority: u16, + host: String, + ttl: u32, + }, // 15 + TXT { + domain: String, + text: Vec, + ttl: u32, + }, //16 + AAAA { + domain: String, + addr: Ipv6Addr, + ttl: u32, + }, // 28 + SRV { + domain: String, + priority: u16, + weight: u16, + port: u16, + target: String, + ttl: u32, + }, // 33 + CAA { + domain: String, + flags: u8, + length: u8, + tag: String, + value: String, + ttl: u32, + }, // 257 +} + +impl DnsRecord { + + pub fn read(buffer: &mut PacketBuffer) -> Result { + let mut domain = String::new(); + buffer.read_qname(&mut domain)?; + + let qtype_num = buffer.read_u16()?; + let qtype = QueryType::from_num(qtype_num); + let _ = buffer.read_u16()?; + let ttl = buffer.read_u32()?; + let data_len = buffer.read_u16()?; + + let header_pos = buffer.pos(); + + trace!("Reading DNS Record TYPE: {:?}", qtype); + + match qtype { + QueryType::A => { + let raw_addr = buffer.read_u32()?; + let addr = Ipv4Addr::new( + ((raw_addr >> 24) & 0xFF) as u8, + ((raw_addr >> 16) & 0xFF) as u8, + ((raw_addr >> 8) & 0xFF) as u8, + (raw_addr & 0xFF) as u8, + ); + + Ok(Self::A { domain, addr, ttl }) + } + QueryType::AAAA => { + let raw_addr1 = buffer.read_u32()?; + let raw_addr2 = buffer.read_u32()?; + let raw_addr3 = buffer.read_u32()?; + let raw_addr4 = buffer.read_u32()?; + let addr = Ipv6Addr::new( + ((raw_addr1 >> 16) & 0xFFFF) as u16, + (raw_addr1 & 0xFFFF) as u16, + ((raw_addr2 >> 16) & 0xFFFF) as u16, + (raw_addr2 & 0xFFFF) as u16, + ((raw_addr3 >> 16) & 0xFFFF) as u16, + (raw_addr3 & 0xFFFF) as u16, + ((raw_addr4 >> 16) & 0xFFFF) as u16, + (raw_addr4 & 0xFFFF) as u16, + ); + + Ok(Self::AAAA { domain, addr, ttl }) + } + QueryType::NS => { + let mut ns = String::new(); + buffer.read_qname(&mut ns)?; + + Ok(Self::NS { + domain, + host: ns, + ttl, + }) + } + QueryType::CNAME => { + let mut cname = String::new(); + buffer.read_qname(&mut cname)?; + + Ok(Self::CNAME { + domain, + host: cname, + ttl, + }) + } + QueryType::SOA => { + let mut mname = String::new(); + buffer.read_qname(&mut mname)?; + + let mut nname = String::new(); + buffer.read_qname(&mut nname)?; + + let serial = buffer.read_u32()?; + let refresh = buffer.read_u32()?; + let retry = buffer.read_u32()?; + let expire = buffer.read_u32()?; + let minimum = buffer.read_u32()?; + + Ok(Self::SOA { + domain, + mname, + nname, + serial, + refresh, + retry, + expire, + minimum, + ttl, + }) + } + QueryType::PTR => { + let mut pointer = String::new(); + buffer.read_qname(&mut pointer)?; + + Ok(Self::PTR { + domain, + pointer, + ttl, + }) + } + QueryType::MX => { + let priority = buffer.read_u16()?; + let mut mx = String::new(); + buffer.read_qname(&mut mx)?; + + Ok(Self::MX { + domain, + priority, + host: mx, + ttl, + }) + } + QueryType::TXT => { + let mut text = Vec::new(); + + loop { + let mut s = String::new(); + buffer.read_string(&mut s)?; + + if s.len() == 0 { + break; + } else { + text.push(s); + } + } + + Ok(Self::TXT { domain, text, ttl }) + } + QueryType::SRV => { + let priority = buffer.read_u16()?; + let weight = buffer.read_u16()?; + let port = buffer.read_u16()?; + + let mut target = String::new(); + buffer.read_qname(&mut target)?; + + Ok(Self::SRV { + domain, + priority, + weight, + port, + target, + ttl, + }) + } + QueryType::CAA => { + let flags = buffer.read()?; + let length = buffer.read()?; + + let mut tag = String::new(); + buffer.read_string_n(&mut tag, length)?; + + let value_len = (data_len as usize) + header_pos - buffer.pos; + let mut value = String::new(); + buffer.read_string_n(&mut value, value_len as u8)?; + + Ok(Self::CAA { + domain, + flags, + length, + tag, + value, + ttl, + }) + } + QueryType::UNKNOWN(_) | _ => { + buffer.step(data_len as usize)?; + + Ok(Self::UNKNOWN { + domain, + qtype: qtype_num, + data_len, + ttl, + }) + } + } + } + + pub fn write(&self, buffer: &mut PacketBuffer) -> Result { + let start_pos = buffer.pos(); + + trace!("Writing DNS Record {:?}", self); + + match *self { + Self::A { + ref domain, + ref addr, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::A.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + buffer.write_u16(4)?; + + let octets = addr.octets(); + buffer.write_u8(octets[0])?; + buffer.write_u8(octets[1])?; + buffer.write_u8(octets[2])?; + buffer.write_u8(octets[3])?; + } + Self::NS { + ref domain, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::NS.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::CNAME { + ref domain, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::CNAME.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::SOA { + ref domain, + ref mname, + ref nname, + serial, + refresh, + retry, + expire, + minimum, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::SOA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(mname)?; + buffer.write_qname(nname)?; + buffer.write_u32(serial)?; + buffer.write_u32(refresh)?; + buffer.write_u32(retry)?; + buffer.write_u32(expire)?; + buffer.write_u32(minimum)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::PTR { + ref domain, + ref pointer, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::NS.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(&pointer)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::MX { + ref domain, + priority, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::MX.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u16(priority)?; + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::TXT { + ref domain, + ref text, + ttl, + } => { + buffer.write_qname(&domain)?; + buffer.write_u16(QueryType::TXT.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + if text.is_empty() { + return Ok(buffer.pos() - start_pos); + } + + for s in text { + buffer.write_u8(s.len() as u8)?; + buffer.write_string(&s)?; + } + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::AAAA { + ref domain, + ref addr, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::AAAA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + buffer.write_u16(16)?; + + for octet in &addr.segments() { + buffer.write_u16(*octet)?; + } + } + Self::SRV { + ref domain, + priority, + weight, + port, + ref target, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::SRV.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u16(priority)?; + buffer.write_u16(weight)?; + buffer.write_u16(port)?; + buffer.write_qname(target)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::CAA { + ref domain, + flags, + length, + ref tag, + ref value, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::CAA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u8(flags)?; + buffer.write_u8(length)?; + buffer.write_string(tag)?; + buffer.write_string(value)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::UNKNOWN { .. } => { + warn!("Skipping record: {self:?}"); + } + } + + Ok(buffer.pos() - start_pos) + } + + pub fn get_ttl(&self) -> u32 { + match *self { + DnsRecord::UNKNOWN { .. } => 0, + DnsRecord::AAAA { ttl, .. } => ttl, + DnsRecord::A { ttl, .. } => ttl, + DnsRecord::NS { ttl, .. } => ttl, + DnsRecord::CNAME { ttl, .. } => ttl, + DnsRecord::SOA { ttl, .. } => ttl, + DnsRecord::PTR { ttl, .. } => ttl, + DnsRecord::MX { ttl, .. } => ttl, + DnsRecord::TXT { ttl, .. } => ttl, + DnsRecord::SRV { ttl, .. } => ttl, + DnsRecord::CAA { ttl, .. } => ttl, + } + } + +} diff --git a/packet/src/result.rs b/src/packet/result.rs similarity index 99% rename from packet/src/result.rs rename to src/packet/result.rs index f33bd7d..41c8ba9 100644 --- a/packet/src/result.rs +++ b/src/packet/result.rs @@ -19,4 +19,4 @@ impl ResultCode { 0 | _ => Self::NOERROR, } } -} \ No newline at end of file +} diff --git a/src/server/binding.rs b/src/server/binding.rs new file mode 100644 index 0000000..1c69651 --- /dev/null +++ b/src/server/binding.rs @@ -0,0 +1,150 @@ +use std::{ + net::{IpAddr, SocketAddr}, + sync::Arc, +}; + +use crate::packet::{buffer::PacketBuffer, Packet, Result}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::{TcpListener, TcpStream, UdpSocket}, +}; +use tracing::trace; + +pub enum Binding { + UDP(Arc), + TCP(TcpListener), +} + +impl Binding { + pub async fn udp(addr: SocketAddr) -> Result { + let socket = UdpSocket::bind(addr).await?; + Ok(Self::UDP(Arc::new(socket))) + } + + pub async fn tcp(addr: SocketAddr) -> Result { + let socket = TcpListener::bind(addr).await?; + Ok(Self::TCP(socket)) + } + + pub fn name(&self) -> &str { + match self { + Binding::UDP(_) => "UDP", + Binding::TCP(_) => "TCP", + } + } + + pub async fn connect(&mut self) -> Result { + match self { + Self::UDP(socket) => { + let mut buf = [0; 512]; + let (_, addr) = socket.recv_from(&mut buf).await?; + Ok(Connection::UDP(socket.clone(), addr, buf)) + } + Self::TCP(socket) => { + let (stream, _) = socket.accept().await?; + Ok(Connection::TCP(stream)) + } + } + } +} + +pub enum Connection { + UDP(Arc, SocketAddr, [u8; 512]), + TCP(TcpStream), +} + +impl Connection { + pub async fn read_packet(&mut self) -> Result { + let data = self.read().await?; + let mut packet_buffer = PacketBuffer::new(data); + + let packet = Packet::from_buffer(&mut packet_buffer)?; + Ok(packet) + } + + pub async fn write_packet(self, mut packet: Packet) -> Result<()> { + let mut packet_buffer = PacketBuffer::new(Vec::new()); + packet.write(&mut packet_buffer)?; + + self.write(packet_buffer.buf).await?; + Ok(()) + } + + pub async fn request_packet(&self, mut packet: Packet, dest: (IpAddr, u16)) -> Result { + let mut packet_buffer = PacketBuffer::new(Vec::new()); + packet.write(&mut packet_buffer)?; + + let data = self.request(packet_buffer.buf, dest).await?; + let mut packet_buffer = PacketBuffer::new(data); + + let packet = Packet::from_buffer(&mut packet_buffer)?; + Ok(packet) + } + + async fn read(&mut self) -> Result> { + trace!("Reading DNS packet"); + match self { + Self::UDP(_, _, src) => Ok(Vec::from(*src)), + Self::TCP(stream) => { + let size = stream.read_u16().await?; + let mut buf = Vec::with_capacity(size as usize); + stream.read_buf(&mut buf).await?; + Ok(buf) + } + } + } + + async fn write(self, mut buf: Vec) -> Result<()> { + trace!("Returning DNS packet"); + match self { + Self::UDP(socket, addr, _) => { + if buf.len() > 512 { + buf[2] = buf[2] | 0x03; + socket.send_to(&buf[0..512], addr).await?; + } else { + socket.send_to(&buf, addr).await?; + } + Ok(()) + } + Self::TCP(mut stream) => { + stream.write_u16(buf.len() as u16).await?; + stream.write(&buf[0..buf.len()]).await?; + Ok(()) + } + } + } + + async fn request(&self, buf: Vec, dest: (IpAddr, u16)) -> Result> { + match self { + Self::UDP(_socket, _addr, _src) => { + let local_addr = "[::]:0".parse::()?; + let socket = UdpSocket::bind(local_addr).await?; + socket.send_to(&buf, dest).await?; + + let mut buf = [0; 512]; + socket.recv_from(&mut buf).await?; + + Ok(Vec::from(buf)) + } + Self::TCP(_stream) => { + let mut stream = TcpStream::connect(dest).await?; + stream.write_u16((buf.len()) as u16).await?; + stream.write_all(&buf[0..buf.len()]).await?; + + stream.readable().await?; + let size = stream.read_u16().await?; + let mut buf = Vec::with_capacity(size as usize); + stream.read_buf(&mut buf).await?; + + Ok(buf) + } + } + } + + // fn pb(buf: &[u8]) { + // for i in 0..buf.len() { + // print!("{:02X?} ", buf[i]); + // } + // println!(""); + // } +} diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 0000000..25076ef --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,3 @@ +mod binding; +mod resolver; +pub mod server; diff --git a/src/server/resolver.rs b/src/server/resolver.rs new file mode 100644 index 0000000..464620c --- /dev/null +++ b/src/server/resolver.rs @@ -0,0 +1,165 @@ +use super::binding::Connection; +use crate::{ + config::Config, + packet::{ + query::QueryType, question::DnsQuestion, result::ResultCode, Packet, + Result, + }, get_time, +}; +use async_recursion::async_recursion; +use moka::future::Cache; +use std::{net::IpAddr, sync::Arc, time::Duration}; +use tracing::{error, trace}; + +pub struct Resolver { + request_id: u16, + connection: Connection, + config: Arc, + cache: Cache, +} + +impl Resolver { + pub fn new( + request_id: u16, + connection: Connection, + config: Arc, + cache: Cache, + ) -> Self { + Self { + request_id, + connection, + config, + cache, + } + } + + async fn lookup_cache(&mut self, qname: &str, qtype: QueryType) -> Option { + let question = DnsQuestion::new(qname.to_string(), qtype); + let Some((packet, date)) = self.cache.get(&question) else { + return None + }; + + let now = get_time(); + let diff = Duration::from_millis(now - date).as_secs() as u32; + + for answer in &packet.answers { + let ttl = answer.get_ttl(); + if diff > ttl { + self.cache.invalidate(&question).await; + return None + } + } + + trace!("Found cached value for {qtype:?} {qname}"); + + Some(packet) + } + + async fn lookup(&mut self, qname: &str, qtype: QueryType, server: (IpAddr, u16)) -> Packet { + let mut packet = Packet::new(); + + packet.header.id = self.request_id; + packet.header.questions = 1; + packet.header.recursion_desired = true; + packet + .questions + .push(DnsQuestion::new(qname.to_string(), qtype)); + + let packet = match self.connection.request_packet(packet, server).await { + Ok(packet) => packet, + Err(e) => { + error!("Failed to complete nameserver request: {e}"); + let mut packet = Packet::new(); + packet.header.rescode = ResultCode::SERVFAIL; + packet + } + }; + + packet + } + + #[async_recursion] + async fn recursive_lookup(&mut self, qname: &str, qtype: QueryType) -> Packet { + let question = DnsQuestion::new(qname.to_string(), qtype); + let mut ns = self.config.get_fallback_ns().clone(); + + if let Some(packet) = self.lookup_cache(qname, qtype).await { return packet } + + loop { + trace!("Attempting lookup of {qtype:?} {qname} with ns {ns}"); + + let ns_copy = ns; + + let server = (ns_copy, 53); + let response = self.lookup(qname, qtype, server).await; + + if !response.answers.is_empty() && response.header.rescode == ResultCode::NOERROR { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + + if response.header.rescode == ResultCode::NXDOMAIN { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + + if let Some(new_ns) = response.get_resolved_ns(qname) { + ns = new_ns; + continue; + } + + let new_ns_name = match response.get_unresolved_ns(qname) { + Some(x) => x, + None => { + self.cache.insert(question, (response.clone(), get_time())).await; + return response + }, + }; + + let recursive_response = self.recursive_lookup(new_ns_name, QueryType::A).await; + + if let Some(new_ns) = recursive_response.get_random_a() { + ns = new_ns; + } else { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + } + } + + pub async fn handle_query(mut self) -> Result<()> { + let mut request = self.connection.read_packet().await?; + + let mut packet = Packet::new(); + packet.header.id = request.header.id; + packet.header.recursion_desired = true; + packet.header.recursion_available = true; + packet.header.response = true; + + if let Some(question) = request.questions.pop() { + trace!("Received query: {question:?}"); + + let result = self.recursive_lookup(&question.name, question.qtype).await; + packet.questions.push(question.clone()); + packet.header.rescode = result.header.rescode; + + for rec in result.answers { + trace!("Answer: {rec:?}"); + packet.answers.push(rec); + } + for rec in result.authorities { + trace!("Authority: {rec:?}"); + packet.authorities.push(rec); + } + for rec in result.resources { + trace!("Resource: {rec:?}"); + packet.resources.push(rec); + } + } else { + packet.header.rescode = ResultCode::FORMERR; + } + + self.connection.write_packet(packet).await?; + Ok(()) + } +} diff --git a/src/server/server.rs b/src/server/server.rs new file mode 100644 index 0000000..e006bb1 --- /dev/null +++ b/src/server/server.rs @@ -0,0 +1,73 @@ +use moka::future::Cache; +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Duration; +use tokio::task::JoinHandle; +use tracing::{error, info}; + +use crate::config::Config; +use crate::packet::question::DnsQuestion; +use crate::packet::{Result, Packet}; + +use super::binding::Binding; +use super::resolver::Resolver; + +pub struct Server { + addr: SocketAddr, + config: Arc, + cache: Cache, +} + +impl Server { + pub async fn new(config: Config) -> Result { + let addr = format!("[::]:{}", config.get_port()).parse::()?; + let cache = Cache::builder() + .time_to_live(Duration::from_secs(60 * 60)) + .max_capacity(1_000) + .build(); + Ok(Self { + addr, + config: Arc::new(config), + cache, + }) + } + + pub async fn run(&self) -> Result<()> { + let tcp = Binding::tcp(self.addr).await?; + let tcp_handle = self.listen(tcp); + + let udp = Binding::udp(self.addr).await?; + let udp_handle = self.listen(udp); + + info!("Fallback DNS Server is set to: {:?}", self.config.get_fallback_ns()); + info!("Listening for TCP and UDP traffic on [::]:{}", self.config.get_port()); + + tokio::join!(tcp_handle) + .0 + .expect("Failed to join tcp thread"); + tokio::join!(udp_handle) + .0 + .expect("Failed to join udp thread"); + Ok(()) + } + + fn listen(&self, mut binding: Binding) -> JoinHandle<()> { + let config = self.config.clone(); + let cache = self.cache.clone(); + tokio::spawn(async move { + let mut id = 0; + loop { + let Ok(connection) = binding.connect().await else { continue }; + info!("Received request on {}", binding.name()); + + let resolver = Resolver::new(id, connection, config.clone(), cache.clone()); + + if let Err(err) = resolver.handle_query().await { + error!("{} request {} failed: {:?}", binding.name(), id, err); + }; + + id += 1; + } + }) + } +}