diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 4e5103b..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-**/target
-.env
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index 7a41547..0000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,2447 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "ahash"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
-dependencies = [
- "getrandom",
- "once_cell",
- "version_check",
-]
-
-[[package]]
-name = "android_system_properties"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
-dependencies = [
- "libc",
-]
-
-[[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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "async-trait"
-version = "0.1.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
-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 = "axum"
-version = "0.6.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8582122b8edba2af43eaf6b80dbfd33f421b5a0eb3a3113d21bc096ac5b44faf"
-dependencies = [
- "async-trait",
- "axum-core",
- "bitflags",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "itoa",
- "matchit",
- "memchr",
- "mime",
- "percent-encoding",
- "pin-project-lite",
- "rustversion",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tower",
- "tower-http",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum-core"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e"
-dependencies = [
- "async-trait",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "mime",
- "rustversion",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
-[[package]]
-name = "base64"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "block-buffer"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "bson"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8746d07211bb12a7c34d995539b4a2acd4e0b0e757de98ce2ab99bcf17443fad"
-dependencies = [
- "ahash",
- "base64 0.13.1",
- "hex",
- "indexmap",
- "lazy_static",
- "rand",
- "serde",
- "serde_bytes",
- "serde_json",
- "time",
- "uuid",
-]
-
-[[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 1.0.16",
- "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 = "chrono"
-version = "0.4.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
-dependencies = [
- "iana-time-zone",
- "num-integer",
- "num-traits",
- "winapi",
-]
-
-[[package]]
-name = "codespan-reporting"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
-dependencies = [
- "termcolor",
- "unicode-width",
-]
-
-[[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 = "convert_case"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
-
-[[package]]
-name = "cookie"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
-dependencies = [
- "percent-encoding",
- "time",
- "version_check",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
-dependencies = [
- "libc",
-]
-
-[[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 = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "cxx"
-version = "1.0.91"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
-dependencies = [
- "cc",
- "cxxbridge-flags",
- "cxxbridge-macro",
- "link-cplusplus",
-]
-
-[[package]]
-name = "cxx-build"
-version = "1.0.91"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
-dependencies = [
- "cc",
- "codespan-reporting",
- "once_cell",
- "proc-macro2",
- "quote",
- "scratch",
- "syn",
-]
-
-[[package]]
-name = "cxxbridge-flags"
-version = "1.0.91"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
-
-[[package]]
-name = "cxxbridge-macro"
-version = "1.0.91"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "darling"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
-dependencies = [
- "darling_core",
- "darling_macro",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn",
-]
-
-[[package]]
-name = "darling_macro"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
-dependencies = [
- "darling_core",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "data-encoding"
-version = "2.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
-
-[[package]]
-name = "derivative"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
-dependencies = [
- "convert_case",
- "proc-macro2",
- "quote",
- "rustc_version 0.4.0",
- "syn",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
-dependencies = [
- "block-buffer",
- "crypto-common",
- "subtle",
-]
-
-[[package]]
-name = "dotenv"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
-
-[[package]]
-name = "enum-as-inner"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[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 = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "futures"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[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-sink"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
-
-[[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-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[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 = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "hermit-abi"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest",
-]
-
-[[package]]
-name = "hostname"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
-dependencies = [
- "libc",
- "match_cfg",
- "winapi",
-]
-
-[[package]]
-name = "http"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
-dependencies = [
- "bytes",
- "http",
- "pin-project-lite",
-]
-
-[[package]]
-name = "http-range-header"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
-
-[[package]]
-name = "httparse"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
-
-[[package]]
-name = "httpdate"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
-
-[[package]]
-name = "hyper"
-version = "0.14.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "iana-time-zone"
-version = "0.1.53"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
-dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "winapi",
-]
-
-[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
-dependencies = [
- "cxx",
- "cxx-build",
-]
-
-[[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-
-[[package]]
-name = "idna"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
-dependencies = [
- "matches",
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "idna"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[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 = "ipconfig"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be"
-dependencies = [
- "socket2",
- "widestring",
- "winapi",
- "winreg",
-]
-
-[[package]]
-name = "ipnet"
-version = "2.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
-
-[[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 = "link-cplusplus"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "linked-hash-map"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
-
-[[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"
-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 = "lru-cache"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
-dependencies = [
- "linked-hash-map",
-]
-
-[[package]]
-name = "mach"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "match_cfg"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
-
-[[package]]
-name = "matches"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
-
-[[package]]
-name = "matchit"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
-
-[[package]]
-name = "md-5"
-version = "0.10.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
-dependencies = [
- "digest",
-]
-
-[[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 = "mime"
-version = "0.3.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
-
-[[package]]
-name = "mime_guess"
-version = "2.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
-dependencies = [
- "mime",
- "unicase",
-]
-
-[[package]]
-name = "mio"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
-dependencies = [
- "libc",
- "log",
- "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 0.4.0",
- "scheduled-thread-pool",
- "skeptic",
- "smallvec",
- "tagptr",
- "thiserror",
- "triomphe",
- "uuid",
-]
-
-[[package]]
-name = "mongodb"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a37fe10c1485a0cd603468e284a1a8535b4ecf46808f5f7de3639a1e1252dbf8"
-dependencies = [
- "async-trait",
- "base64 0.13.1",
- "bitflags",
- "bson",
- "chrono",
- "derivative",
- "derive_more",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-util",
- "hex",
- "hmac",
- "lazy_static",
- "md-5",
- "pbkdf2",
- "percent-encoding",
- "rand",
- "rustc_version_runtime",
- "rustls",
- "rustls-pemfile",
- "serde",
- "serde_bytes",
- "serde_with",
- "sha-1",
- "sha2",
- "socket2",
- "stringprep",
- "strsim",
- "take_mut",
- "thiserror",
- "tokio",
- "tokio-rustls",
- "tokio-util",
- "trust-dns-proto",
- "trust-dns-resolver",
- "typed-builder",
- "uuid",
- "webpki-roots",
-]
-
-[[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-integer"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
-dependencies = [
- "autocfg",
-]
-
-[[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 = "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"
-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 = "pbkdf2"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
-dependencies = [
- "digest",
-]
-
-[[package]]
-name = "percent-encoding"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
-
-[[package]]
-name = "pin-project"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pin-project-lite"
-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 = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[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 = "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 = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quote"
-version = "1.0.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "resolv-conf"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
-dependencies = [
- "hostname",
- "quick-error",
-]
-
-[[package]]
-name = "ring"
-version = "0.16.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin",
- "untrusted",
- "web-sys",
- "winapi",
-]
-
-[[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
-dependencies = [
- "semver 0.9.0",
-]
-
-[[package]]
-name = "rustc_version"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
-dependencies = [
- "semver 1.0.16",
-]
-
-[[package]]
-name = "rustc_version_runtime"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f"
-dependencies = [
- "rustc_version 0.2.3",
- "semver 0.9.0",
-]
-
-[[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 = "rustls"
-version = "0.20.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
-dependencies = [
- "log",
- "ring",
- "sct",
- "webpki",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
-dependencies = [
- "base64 0.21.0",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
-
-[[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]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "scratch"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c"
-
-[[package]]
-name = "sct"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver"
-version = "1.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
-[[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_bytes"
-version = "0.11.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294"
-dependencies = [
- "serde",
-]
-
-[[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.94"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
-dependencies = [
- "indexmap",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_path_to_error"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db0969fff533976baadd92e08b1d102c5a3d8a8049eadfd69d4d1e3c5b2ed189"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_with"
-version = "1.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
-dependencies = [
- "serde",
- "serde_with_macros",
-]
-
-[[package]]
-name = "serde_with_macros"
-version = "1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
-dependencies = [
- "darling",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "sha-1"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "sha2"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
-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"
-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 = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
-[[package]]
-name = "stringprep"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "subtle"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
-
-[[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 = "sync_wrapper"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
-
-[[package]]
-name = "tagptr"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
-
-[[package]]
-name = "take_mut"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
-
-[[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 = "termcolor"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
-dependencies = [
- "winapi-util",
-]
-
-[[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 = "time"
-version = "0.3.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
-dependencies = [
- "itoa",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
-
-[[package]]
-name = "time-macros"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
-dependencies = [
- "time-core",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[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 = "tokio-rustls"
-version = "0.23.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
-dependencies = [
- "rustls",
- "tokio",
- "webpki",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-io",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project",
- "pin-project-lite",
- "tokio",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-cookies"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984"
-dependencies = [
- "async-trait",
- "axum-core",
- "cookie",
- "futures-util",
- "http",
- "parking_lot",
- "pin-project-lite",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
-dependencies = [
- "bitflags",
- "bytes",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "http-range-header",
- "httpdate",
- "mime",
- "mime_guess",
- "percent-encoding",
- "pin-project-lite",
- "tokio",
- "tokio-util",
- "tower",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
-
-[[package]]
-name = "tower-service"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
-
-[[package]]
-name = "tracing"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
-dependencies = [
- "cfg-if",
- "log",
- "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 = "trust-dns-proto"
-version = "0.21.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d"
-dependencies = [
- "async-trait",
- "cfg-if",
- "data-encoding",
- "enum-as-inner",
- "futures-channel",
- "futures-io",
- "futures-util",
- "idna 0.2.3",
- "ipnet",
- "lazy_static",
- "log",
- "rand",
- "smallvec",
- "thiserror",
- "tinyvec",
- "tokio",
- "url",
-]
-
-[[package]]
-name = "trust-dns-resolver"
-version = "0.21.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558"
-dependencies = [
- "cfg-if",
- "futures-util",
- "ipconfig",
- "lazy_static",
- "log",
- "lru-cache",
- "parking_lot",
- "resolv-conf",
- "smallvec",
- "thiserror",
- "tokio",
- "trust-dns-proto",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
-
-[[package]]
-name = "typed-builder"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "typenum"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
-
-[[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-bidi"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-width"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
-
-[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
-[[package]]
-name = "url"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
-dependencies = [
- "form_urlencoded",
- "idna 0.3.0",
- "percent-encoding",
-]
-
-[[package]]
-name = "uuid"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
-dependencies = [
- "getrandom",
- "serde",
-]
-
-[[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 = "want"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
-dependencies = [
- "log",
- "try-lock",
-]
-
-[[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 = "webpki"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.22.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
-dependencies = [
- "webpki",
-]
-
-[[package]]
-name = "wepoll-ffi"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "widestring"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
-
-[[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-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"
-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"
-
-[[package]]
-name = "winreg"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "wrapper"
-version = "0.1.0"
-dependencies = [
- "async-recursion",
- "axum",
- "bytes",
- "dotenv",
- "futures",
- "moka",
- "mongodb",
- "rand",
- "serde",
- "serde_json",
- "tokio",
- "tower",
- "tower-cookies",
- "tower-http",
- "tracing",
- "tracing-subscriber",
-]
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index c114ff5..0000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,37 +0,0 @@
-[package]
-name = "wrapper"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-# Blazingly fast runtime
-tokio = { version = "1", features = ["full"] }
-tracing-subscriber = "0.3.16"
-tracing = "0.1.37"
-
-# Allow recursion inside tokio async
-async-recursion = "1"
-
-# DNS Caching Layer
-moka = { version = "0.10.0", features = ["future"] }
-
-# Mongodb
-mongodb = { version = "2.4", features = ["tokio-sync"] }
-futures = "0.3.26"
-
-# Convert values to json for Mongodb
-serde = { version = "1.0", features = ["derive"] }
-
-# Reading env vars from .env
-dotenv = "0.15.0"
-
-# For the meme records
-rand = "0.8.5"
-
-# For the http web frontend
-axum = "0.6.4"
-tower-http = { version = "0.4.0", features = ["fs"] }
-tower-cookies = "0.9.0"
-tower = "0.4.13"
-bytes = "1.4.0"
-serde_json = "1"
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6c2a640
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+CC = gcc
+
+INCFLAGS = -Isrc
+
+CCFLAGS = -std=c17 -Wall -Wextra -O2
+CCFLAGS += $(INCFLAGS)
+
+LDFLAGS += $(INCFLAGS)
+
+BIN = bin
+APP = $(BIN)/app
+SRC = $(shell find src -name "*.c")
+OBJ = $(SRC:%.c=$(BIN)/%.o)
+
+.PHONY: clean build
+
+dirs:
+ mkdir -p ./$(BIN)
+ mkdir -p ./$(BIN)/src
+ mkdir -p ./$(BIN)/src/io
+ mkdir -p ./$(BIN)/src/packet
+ mkdir -p ./$(BIN)/src/server
+
+run: build
+ $(APP)
+
+build: dirs ${OBJ}
+ ${CC} -o $(APP) $(filter %.o,$^) $(LDFLAGS)
+
+$(BIN)/%.o: %.c
+ $(CC) -o $@ -c $< $(CCFLAGS)
+
+clean:
+ rm -rf $(APP)
+ rm -rf $(BIN)
diff --git a/bin/app b/bin/app
new file mode 100755
index 0000000..1cf9d91
Binary files /dev/null and b/bin/app differ
diff --git a/bin/src/io/log.o b/bin/src/io/log.o
new file mode 100644
index 0000000..b93da40
Binary files /dev/null and b/bin/src/io/log.o differ
diff --git a/bin/src/main.o b/bin/src/main.o
new file mode 100644
index 0000000..ab9cdf4
Binary files /dev/null and b/bin/src/main.o differ
diff --git a/bin/src/packet/buffer.o b/bin/src/packet/buffer.o
new file mode 100644
index 0000000..189d786
Binary files /dev/null and b/bin/src/packet/buffer.o differ
diff --git a/bin/src/packet/header.o b/bin/src/packet/header.o
new file mode 100644
index 0000000..9754cda
Binary files /dev/null and b/bin/src/packet/header.o differ
diff --git a/bin/src/packet/packet.o b/bin/src/packet/packet.o
new file mode 100644
index 0000000..9cac77a
Binary files /dev/null and b/bin/src/packet/packet.o differ
diff --git a/bin/src/packet/question.o b/bin/src/packet/question.o
new file mode 100644
index 0000000..834a579
Binary files /dev/null and b/bin/src/packet/question.o differ
diff --git a/bin/src/packet/record.o b/bin/src/packet/record.o
new file mode 100644
index 0000000..986a54d
Binary files /dev/null and b/bin/src/packet/record.o differ
diff --git a/bin/src/server/addr.o b/bin/src/server/addr.o
new file mode 100644
index 0000000..6d2e1b0
Binary files /dev/null and b/bin/src/server/addr.o differ
diff --git a/bin/src/server/binding.o b/bin/src/server/binding.o
new file mode 100644
index 0000000..1ff5f6e
Binary files /dev/null and b/bin/src/server/binding.o differ
diff --git a/bin/src/server/resolver.o b/bin/src/server/resolver.o
new file mode 100644
index 0000000..40fa438
Binary files /dev/null and b/bin/src/server/resolver.o differ
diff --git a/bin/src/server/server.o b/bin/src/server/server.o
new file mode 100644
index 0000000..44e1cd8
Binary files /dev/null and b/bin/src/server/server.o differ
diff --git a/public/css/home.css b/public/css/home.css
deleted file mode 100644
index 2548a28..0000000
--- a/public/css/home.css
+++ /dev/null
@@ -1,40 +0,0 @@
-span {
- margin-top: 5rem;
- margin-bottom: 1rem;
- width: 45rem;
- font-size: 2em;
-}
-
-#new {
- display: flex;
- justify-content: center;
- width: 100%;
- padding-top: 2rem;
- padding-bottom: 1rem;
- border-bottom: solid 1px var(--gray);
-}
-
-#new input, .block {
- border-radius: 1rem 0 0 1rem;
- width: 40rem;
-}
-
-.block {
- width: 33em;
-}
-
-#new button {
- border-radius: 0 1rem 1rem 0;
-}
-
-.domain {
- margin-top: 2rem;
-}
-
-.domain .delete {
- border-radius: 0 1rem 1rem 0;
-}
-
-.domain .edit {
- border-radius: 0;
-}
\ No newline at end of file
diff --git a/public/css/login.css b/public/css/login.css
deleted file mode 100644
index 2be7c13..0000000
--- a/public/css/login.css
+++ /dev/null
@@ -1,18 +0,0 @@
-#login {
- margin-top: 20em;
-}
-
-#logo {
- font-size: 6em;
- font-weight: 750;
- font-family: bold;
- margin-bottom: 2rem;
-}
-
-form {
- width: 30rem;
-}
-
-form input {
- width: 100%;
-}
\ No newline at end of file
diff --git a/public/css/main.css b/public/css/main.css
deleted file mode 100644
index 971e2bb..0000000
--- a/public/css/main.css
+++ /dev/null
@@ -1,119 +0,0 @@
-:root {
- --dark: #222428;
- --dark-alternate: #2b2e36;
- --header: #1e1e22;
-
- --accent: #8849f5;
- --accent-alternate: #6829d5;
- --gray: #2f2f3f;
- --main: #ffffff;
- --main-alternate: #cccccc;
-}
-
-* {
- padding: 0;
- margin: 0;
-}
-
-@font-face {
- font-family: main;
- src: url("../fonts/helvetica.ttf") format("truetype");
- font-display: swap;
-}
-
-@font-face {
- font-family: bold;
- src: url("../fonts/overpass-bold.otf") format("opentype");
- font-display: swap;
-}
-
-@font-face {
- font-family: bold-italic;
- src: url("../fonts/overpass-bold-italic.otf") format("opentype");
- font-display: swap;
-}
-
-html {
- background-color: var(--dark);
- font-family: main;
- color: var(--main);
- width: 100%;
- height: 100%;
-}
-
-body {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-
-.accent {
- color: var(--accent);
-}
-
-.fill {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-
-input, button, .block {
- all: unset;
- display: inline-block;
- font: main;
- background-color: var(--dark-alternate);
- font-size: 1rem;
- padding: 1rem;
- border-radius: 1rem;
- margin-bottom: 20px;
-}
-
-button {
- background-color: var(--accent);
- width: 5em;
- text-align: center;
-}
-
-button:hover {
- cursor: pointer;
- background-color: var(--accent-alternate);
-}
-
-.delete {
- background-color: #f54842;
-}
-
-.delete:hover {
- cursor: pointer;
- background-color: #d52822;
-}
-
-form {
- display: flex;
- flex-direction: column;
-}
-
-#header {
- width: calc(100% - 4rem);
- background-color: var(--header);
- border-bottom: solid 1px var(--gray);
- padding: 1rem;
- padding-left: 3rem;
-}
-
-#logo {
- font-size: 2em;
- font-weight: 500;
- font-family: bold;
-}
-
-#title {
- font-size: 2em;
- font-weight: 300;
- font-family: sans-serif;
- padding-left: 1em;
-}
\ No newline at end of file
diff --git a/public/css/record.css b/public/css/record.css
deleted file mode 100644
index 3dc257a..0000000
--- a/public/css/record.css
+++ /dev/null
@@ -1,67 +0,0 @@
-#buttons {
- margin-top: 2rem;
- width: 50rem;
-}
-
-#buttons button {
- margin: 0;
- margin-right: 2rem;
- border-radius: 10px;
- width: auto;
- padding: .75rem 1rem;
-}
-
-.record {
- width: 50rem;
- background-color: var(--header);
- padding: 1rem;
- margin-top: 2rem;
-}
-
-.header {
- display: flex;
- align-items: center;
- margin-bottom: 1rem;
-}
-
-.header span {
- font-family: bold;
-}
-
-.header button {
- margin: 0;
- margin-left: 2rem;
- padding: .5rem 1rem;
- width: auto;
- border-radius: 5px;
-}
-
-.type {
- margin-right: 1rem;
- background-color: var(--accent);
- padding: .25rem .5rem;
- border-radius: 5px;
-}
-
-.domain {
- color: var(--main-alternate);
- flex-grow: 1;
-}
-
-.properties {
- display: flex;
- flex-direction: column;
-}
-
-.poperty {
- display: flex;
- flex-direction: row;
- border-bottom: solid 1px var(--gray);
- margin-top: 1rem;
-}
-
-.key {
- font-family: bold;
- width: 5rem;
-}
-
diff --git a/public/domain.html b/public/domain.html
deleted file mode 100644
index ba22eaf..0000000
--- a/public/domain.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- Wrapper - Records
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/public/fonts/helvetica-bold.ttf b/public/fonts/helvetica-bold.ttf
deleted file mode 100644
index 332b66c..0000000
Binary files a/public/fonts/helvetica-bold.ttf and /dev/null differ
diff --git a/public/fonts/helvetica.ttf b/public/fonts/helvetica.ttf
deleted file mode 100644
index 718f22d..0000000
Binary files a/public/fonts/helvetica.ttf and /dev/null differ
diff --git a/public/fonts/overpass-bold-italic.otf b/public/fonts/overpass-bold-italic.otf
deleted file mode 100644
index f4929ae..0000000
Binary files a/public/fonts/overpass-bold-italic.otf and /dev/null differ
diff --git a/public/fonts/overpass-bold.otf b/public/fonts/overpass-bold.otf
deleted file mode 100644
index 962a5d7..0000000
Binary files a/public/fonts/overpass-bold.otf and /dev/null differ
diff --git a/public/home.html b/public/home.html
deleted file mode 100644
index 9efb21e..0000000
--- a/public/home.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- Wrapper - Domains
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/public/js/api.js b/public/js/api.js
deleted file mode 100644
index 7a29d65..0000000
--- a/public/js/api.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const endpoint = '/api'
-
-const request = async (url, method, body) => {
-
- let response;
-
- if (method == 'GET') {
- response = await fetch(endpoint + url, {
- method,
- headers: {
- 'Content-Type': 'application/json'
- }
- });
- } else {
- response = await fetch(endpoint + url, {
- method,
- body: JSON.stringify(body),
- headers: {
- 'Content-Type': 'application/json'
- }
- });
- }
-
- if (response.status == 401) {
- location.href = '/login'
- }
- const contentType = response.headers.get("content-type");
- if (contentType && contentType.indexOf("application/json") !== -1) {
- const json = await response.json()
- return { status: response.status, msg: json.msg, json }
- } else {
- const msg = await response.text();
- return { status: response.status, msg }
- }
-}
-
-export const login = async (user, pass) => {
- return await request('/login', 'POST', {user, pass})
-}
-
-export const domains = async () => {
- return await request('/domains', 'GET')
-}
-
-export const del_domain = async (domain) => {
- return await request('/domains', 'DELETE', {domain})
-}
-
-export const records = async (domain) => {
- return await request(`/records?domain=${domain}`, 'GET')
-}
\ No newline at end of file
diff --git a/public/js/components.js b/public/js/components.js
deleted file mode 100644
index 00def5f..0000000
--- a/public/js/components.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { div, parse, span } from './main.js';
-
-export function header(title) {
- return div({id: 'header'},
- span({id: 'logo', class: 'accent'},
- parse("Wrapper")
- ),
- span({id: 'title'},
- parse(title)
- ),
- )
-}
\ No newline at end of file
diff --git a/public/js/domain.js b/public/js/domain.js
deleted file mode 100644
index 36af422..0000000
--- a/public/js/domain.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { del_domain, domains, records } from './api.js'
-import { header } from './components.js'
-import { body, parse, div, input, button, span, is_domain } from './main.js';
-
-function render(domain, records) {
-
- let divs = []
- for (const record of records) {
- divs.push(gen_record(record))
- }
-
- document.body.replaceWith(
- body({},
- header(domain),
- div({id: 'buttons'},
- button({onclick: (event) => {
- location.href = '/home'
- }}, parse("Home")),
- button({}, parse("New Record")),
- ),
- ...divs
- )
- )
-}
-
-function gen_record(record) {
- let domain = record.domain
- let prefix = record.prefix
-
- if (prefix.length > 0) {
- prefix = prefix + '.'
- }
-
- let type = Object.keys(record.record)[0]
- let data = record.record[type]
-
- let divs = []
- for (const key in data) {
- let disp_key;
- if (key == 'ttl') {
- disp_key = 'TTL'
- } else {
- disp_key = upper(key)
- }
- divs.push(
- div({class: 'poperty'},
- div({class: 'key'}, parse(disp_key)),
- div({class: 'value'}, parse(data[key])),
- )
- )
- }
-
- return div({class: 'record'},
- div({class: 'header'},
- span({class: 'type'}, parse(type)),
- span({class: 'prefix'}, parse(prefix)),
- span({class: 'domain'}, parse(domain)),
- button({}, parse("Edit")),
- button({class: 'delete'}, parse("Delete"))
- ),
- div({class: 'properties'},
- ...divs
- )
- )
-}
-
-function upper(string) {
- return string.charAt(0).toUpperCase() + string.slice(1);
-}
-
-async function init() {
-
- const params = new Proxy(new URLSearchParams(window.location.search), {
- get: (searchParams, prop) => searchParams.get(prop),
- });
-
- let domain = params.domain;
-
- if (!is_domain(domain)) {
- location.href = '/home'
- return
- }
-
- let res = await records(domain);
-
- if (res.status !== 200) {
- alert(res.msg)
- return
- }
-
- render(domain, res.json)
-
-}
-
-init()
\ No newline at end of file
diff --git a/public/js/home.js b/public/js/home.js
deleted file mode 100644
index f615632..0000000
--- a/public/js/home.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { del_domain, domains } from './api.js'
-import { header } from './components.js'
-import { body, parse, div, input, button, span, is_domain } from './main.js';
-
-function render(domains) {
- document.body.replaceWith(
- body({},
- header('domains'),
- div({id: 'new'},
- input({
- type: 'text',
- name: 'domain',
- id: 'domain',
- placeholder: 'Type domain name to create new records',
- autocomplete: "off",
- }),
- button({onclick: () => {
- let domain = document.getElementById('domain').value
-
- if (!is_domain(domain)) {
- alert("Invalid domain")
- return
- }
-
- location.href = '/domain?domain='+domain
- }},
- parse("Create")
- )
- ),
- ...domain(domains)
- )
- )
-}
-
-function domain(domains) {
- let divs = []
- for (const domain of domains) {
- divs.push(
- div({class: 'domain'},
- div({class: 'block'},
- parse(domain)
- ),
- button({class: 'edit', onclick: (event) => {
- console.log(event.target.parentElement)
- let domain = event
- .target
- .parentElement
- .getElementsByClassName('block')[0]
- .innerText
-
- if (!is_domain(domain)) {
- alert("Invalid domain")
- return
- }
-
- location.href = '/domain?domain='+domain
- }},
- parse("Edit")
- ),
- button({class: 'delete', onclick: async () => {
- let res = await del_domain(domain)
-
- if (res.status != 204) {
- alert(res.msg)
- return
- }
-
- location.reload()
- }},
- parse("Delete")
- )
- )
- )
- }
- return divs
-}
-
-async function init() {
-
- let res = await domains();
-
- if (res.status !== 200) {
- alert(res.msg)
- return
- }
-
- render(res.json)
-
-}
-
-init()
\ No newline at end of file
diff --git a/public/js/login.js b/public/js/login.js
deleted file mode 100644
index 3bd64ad..0000000
--- a/public/js/login.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { body, div, form, input, p, parse, span} from './main.js'
-import { login } from './api.js'
-
-function render() {
- document.body.replaceWith(
- body({},
- div({id: 'login', class: 'fill'},
- span({id: 'logo'},
- span({class: 'accent'}, parse('Wrapper'))
- ),
- form({autocomplete: "off"},
- input({
- type: 'text',
- name: 'user',
- id: 'user',
- placeholder: 'Username',
- autofocus: 1
- }),
- input({
- type: 'password',
- name: 'pass',
- id: 'pass',
- placeholder: 'Password',
- onkeydown: async (event) => {
- if (event.key == 'Enter') {
- event.preventDefault()
- let user = document.getElementById('user').value
- let pass = document.getElementById('pass').value
-
- let res = await login(user, pass)
-
- if (res.status === 200) {
- location.href = '/home'
- }
- }
- }
- })
- )
- )
- )
- )
-}
-
-render()
diff --git a/public/js/main.js b/public/js/main.js
deleted file mode 100644
index 615b3d6..0000000
--- a/public/js/main.js
+++ /dev/null
@@ -1,136 +0,0 @@
-function createElement(name, attrs, ...children) {
- const el = document.createElement(name);
-
- for (const attr in attrs) {
- if(attr.startsWith("on")) {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr])
- }
- }
-
- for (const child of children) {
- if (child == null) {
- continue
- }
- el.appendChild(child)
- }
-
- return el
-}
-
-export function createElementNS(name, attrs, ...children) {
- var svgns = "http://www.w3.org/2000/svg";
- var el = document.createElementNS(svgns, name);
-
- for (const attr in attrs) {
- if(attr.startsWith("on")) {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr])
- }
- }
-
- for (const child of children) {
- if (child == null) {
- continue
- }
- el.appendChild(child)
- }
-
- return el
-}
-
-export function p(attrs, ...children) {
- return createElement("p", attrs, ...children)
-}
-
-export function span(attrs, ...children) {
- return createElement("span", attrs, ...children)
-}
-
-export function div(attrs, ...children) {
- return createElement("div", attrs, ...children)
-}
-
-export function a(attrs, ...children) {
- return createElement("a", attrs, ...children)
-}
-
-export function i(attrs, ...children) {
- return createElement("i", attrs, ...children)
-}
-
-export function form(attrs, ...children) {
- return createElement("form", attrs, ...children)
-}
-
-export function img(alt, attrs, ...children) {
- attrs['onerror'] = (event) => event.target.remove()
- attrs['alt'] = alt
- return createElement("img", attrs, ...children)
-}
-
-export function input(attrs, ...children) {
- return createElement("input", attrs, ...children)
-}
-
-export function button(attrs, ...children) {
- return createElement("button", attrs, ...children)
-}
-
-export function path(attrs, ...children) {
- return createElementNS("path", attrs, ...children)
-}
-
-export function svg(attrs, ...children) {
- return createElementNS("svg", attrs, ...children)
-}
-
-export function body(attrs, ...children) {
- return createElement("body", attrs, ...children)
-}
-
-export function textarea(attrs, ...children) {
- return createElement("textarea", attrs, ...children)
-}
-
-export function parse(input) {
- const pattern = /^[ a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/;
-
- input = input + '';
-
- if (!pattern.test(input)) {
- return null;
- }
-
- const sanitized = input.replace(//g, '>');
- return document.createRange().createContextualFragment(sanitized);
-}
-
-export function is_domain(domain) {
- domain = domain.toLowerCase()
-
- const pattern = /^[a-z0-9_\-.]*$/;
- if (!pattern.test(domain)) {
- return false
- }
-
- let parts = domain.split('.').reverse()
- for (const part of parts) {
- if (part.length < 1) {
- return false
- }
- }
-
- if (parts.length < 2 || parts[0].length < 2) {
- return false
- }
-
- const tld_pattern = /^[a-z]*$/;
- if (!tld_pattern.test(parts[0])) {
- return false
- }
-
- return true
-}
\ No newline at end of file
diff --git a/public/login.html b/public/login.html
deleted file mode 100644
index b03324b..0000000
--- a/public/login.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- Wrapper - Login
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/public/robots.txt b/public/robots.txt
deleted file mode 100644
index b1b4ec3..0000000
--- a/public/robots.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-User-agent: Googlebot
-Disallow: /api
-
-User-agent: Googlebot
-User-agent: AdsBot-Google
-Disallow: /api
-
-User-agent: *
-Disallow: /api
\ No newline at end of file
diff --git a/src/config.rs b/src/config.rs
deleted file mode 100644
index 547e853..0000000
--- a/src/config.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use std::{env, net::IpAddr, str::FromStr, fmt::Display};
-
-#[derive(Clone)]
-pub struct Config {
- pub dns_fallback: IpAddr,
- pub dns_port: u16,
- pub dns_cache_size: u64,
-
- pub db_host: String,
- pub db_port: u16,
- pub db_user: String,
- pub db_pass: String,
-
- pub web_user: String,
- pub web_pass: String,
- pub web_port: u16,
-}
-
-impl Config {
- pub fn new() -> Self {
- let dns_port = Self::get_var::("WRAPPER_DNS_PORT", 53);
- let dns_fallback = Self::get_var::("WRAPPER_FALLBACK_DNS", [9, 9, 9, 9].into());
- let dns_cache_size = Self::get_var::("WRAPPER_CACHE_SIZE", 1000);
-
- let db_host = Self::get_var::("WRAPPER_DB_HOST", String::from("localhost"));
- let db_port = Self::get_var::("WRAPPER_DB_PORT", 27017);
- let db_user = Self::get_var::("WRAPPER_DB_USER", String::from("root"));
- let db_pass = Self::get_var::("WRAPPER_DB_PASS", String::from(""));
-
- let web_user = Self::get_var::("WRAPPER_WEB_USER", String::from("admin"));
- let web_pass = Self::get_var::("WRAPPER_WEB_PASS", String::from("wrapper"));
- let web_port = Self::get_var::("WRAPPER_WEB_PORT", 80);
-
- Self {
- dns_fallback,
- dns_port,
- dns_cache_size,
-
- db_host,
- db_port,
- db_user,
- db_pass,
-
- web_user,
- web_pass,
- web_port,
- }
- }
-
- fn get_var(name: &str, default: T) -> T
- where
- T: FromStr + Display,
- {
- let env = env::var(name).unwrap_or(format!("{default}"));
- env.parse::().unwrap_or(default)
- }
-}
diff --git a/src/database/mod.rs b/src/database/mod.rs
deleted file mode 100644
index 0d81dc3..0000000
--- a/src/database/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-use futures::TryStreamExt;
-use mongodb::{
- bson::doc,
- options::{ClientOptions, Credential, ServerAddress},
- Client,
-};
-use serde::{Deserialize, Serialize};
-use tracing::info;
-
-use crate::{
- config::Config,
- dns::packet::{query::QueryType, record::DnsRecord},
-};
-
-use crate::Result;
-
-#[derive(Clone)]
-pub struct Database {
- client: Client,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct StoredRecord {
- record: DnsRecord,
- domain: String,
- prefix: String,
-}
-
-impl StoredRecord {
- fn get_domain_parts(domain: &str) -> (String, String) {
- let parts: Vec<&str> = domain.split(".").collect();
- let len = parts.len();
- if len == 1 {
- (String::new(), String::from(parts[0]))
- } else if len == 2 {
- (String::new(), String::from(parts.join(".")))
- } else {
- (
- String::from(parts[0..len - 2].join(".")),
- String::from(parts[len - 2..len].join(".")),
- )
- }
- }
-}
-
-impl From for StoredRecord {
- fn from(record: DnsRecord) -> Self {
- let (prefix, domain) = Self::get_domain_parts(&record.get_domain());
- Self {
- record,
- domain,
- prefix,
- }
- }
-}
-
-impl Into for StoredRecord {
- fn into(self) -> DnsRecord {
- self.record
- }
-}
-
-impl Database {
- pub async fn new(config: Config) -> Result {
- let options = ClientOptions::builder()
- .hosts(vec![ServerAddress::Tcp {
- host: config.db_host,
- port: Some(config.db_port),
- }])
- .credential(
- Credential::builder()
- .username(config.db_user)
- .password(config.db_pass)
- .build(),
- )
- .max_pool_size(100)
- .app_name(String::from("wrapper"))
- .build();
-
- let client = Client::with_options(options)?;
-
- client
- .database("wrapper")
- .run_command(doc! {"ping": 1}, None)
- .await?;
-
- info!("Connection to mongodb successfully");
-
- Ok(Database { client })
- }
-
- pub async fn get_records(&self, domain: &str, qtype: QueryType) -> Result> {
- let (prefix, domain) = StoredRecord::get_domain_parts(domain);
- Ok(self
- .get_domain(&domain)
- .await?
- .into_iter()
- .filter(|r| r.prefix == prefix)
- .filter(|r| {
- let rqtype = r.record.get_qtype();
- if qtype == QueryType::A {
- return rqtype == QueryType::A || rqtype == QueryType::AR;
- } else if qtype == QueryType::AAAA {
- return rqtype == QueryType::AAAA || rqtype == QueryType::AAAAR;
- } else {
- r.record.get_qtype() == qtype
- }
- })
- .map(|r| r.into())
- .collect())
- }
-
- pub async fn get_domain(&self, domain: &str) -> Result> {
- let db = self.client.database("wrapper");
- let col = db.collection::(domain);
-
- let filter = doc! { "domain": domain };
- let mut cursor = col.find(filter, None).await?;
-
- let mut records = Vec::new();
- while let Some(record) = cursor.try_next().await? {
- records.push(record);
- }
-
- Ok(records)
- }
-
- pub async fn add_record(&self, record: DnsRecord) -> Result<()> {
- let record = StoredRecord::from(record);
- let db = self.client.database("wrapper");
- let col = db.collection::(&record.domain);
- col.insert_one(record, None).await?;
- Ok(())
- }
-
- pub async fn get_domains(&self) -> Result> {
- let db = self.client.database("wrapper");
- Ok(db.list_collection_names(None).await?)
- }
-
- pub async fn delete_domain(&self, domain: String) -> Result<()> {
- let db = self.client.database("wrapper");
- let col = db.collection::(&domain);
- Ok(col.drop(None).await?)
- }
-}
diff --git a/src/dns/binding.rs b/src/dns/binding.rs
deleted file mode 100644
index 4c7e15f..0000000
--- a/src/dns/binding.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use std::{
- net::{IpAddr, SocketAddr},
- sync::Arc,
-};
-
-use super::packet::{buffer::PacketBuffer, Packet};
-use crate::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)
- }
- }
- }
-}
diff --git a/src/dns/mod.rs b/src/dns/mod.rs
deleted file mode 100644
index 6f1e59e..0000000
--- a/src/dns/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-mod binding;
-pub mod packet;
-mod resolver;
-pub mod server;
diff --git a/src/dns/packet/buffer.rs b/src/dns/packet/buffer.rs
deleted file mode 100644
index 058156e..0000000
--- a/src/dns/packet/buffer.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use crate::Result;
-
-pub struct PacketBuffer {
- pub buf: Vec,
- pub pos: usize,
- pub size: usize,
-}
-
-impl PacketBuffer {
- pub fn new(buf: Vec) -> Self {
- Self {
- size: buf.len(),
- buf,
- pos: 0,
- }
- }
-
- pub fn pos(&self) -> usize {
- self.pos
- }
-
- pub fn step(&mut self, steps: usize) -> Result<()> {
- self.pos += steps;
-
- Ok(())
- }
-
- pub fn seek(&mut self, pos: usize) -> Result<()> {
- self.pos = pos;
-
- Ok(())
- }
-
- pub fn read(&mut self) -> Result {
- if self.pos >= self.size {
- return Err("Tried to read past end of buffer".into());
- }
- let res = self.buf[self.pos];
- self.pos += 1;
- Ok(res)
- }
-
- pub fn get(&mut self, pos: usize) -> Result {
- if pos >= self.size {
- return Err("Tried to read past end of buffer".into());
- }
- Ok(self.buf[pos])
- }
-
- pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> {
- if start + len >= self.size {
- return Err("Tried to read past end of buffer".into());
- }
- Ok(&self.buf[start..start + len])
- }
-
- pub fn read_u16(&mut self) -> Result {
- let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
-
- Ok(res)
- }
-
- pub fn read_u32(&mut self) -> Result {
- let res = ((self.read()? as u32) << 24)
- | ((self.read()? as u32) << 16)
- | ((self.read()? as u32) << 8)
- | (self.read()? as u32);
-
- Ok(res)
- }
-
- pub fn read_qname(&mut self, outstr: &mut String) -> Result<()> {
- let mut pos = self.pos();
- let mut jumped = false;
-
- let mut delim = "";
- let max_jumps = 5;
- let mut jumps_performed = 0;
- loop {
- // Dns Packets are untrusted data, so we need to be paranoid. Someone
- // can craft a packet with a cycle in the jump instructions. This guards
- // against such packets.
- if jumps_performed > max_jumps {
- return Err(format!("Limit of {max_jumps} jumps exceeded").into());
- }
-
- let len = self.get(pos)?;
-
- if (len & 0xC0) == 0xC0 {
- if !jumped {
- self.seek(pos + 2)?;
- }
-
- let b2 = self.get(pos + 1)? as u16;
- let offset = (((len as u16) ^ 0xC0) << 8) | b2;
- pos = offset as usize;
- jumped = true;
- jumps_performed += 1;
- continue;
- }
-
- pos += 1;
-
- if len == 0 {
- break;
- }
-
- outstr.push_str(delim);
-
- let str_buffer = self.get_range(pos, len as usize)?;
- outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase());
-
- delim = ".";
-
- pos += len as usize;
- }
-
- if !jumped {
- self.seek(pos)?;
- }
-
- Ok(())
- }
-
- 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<()> {
- if self.size < self.pos {
- self.size = self.pos;
- }
-
- if self.buf.len() <= self.size {
- self.buf.resize(self.size + 1, 0x00);
- }
-
- self.buf[self.pos] = val;
- self.pos += 1;
- Ok(())
- }
-
- pub fn write_u8(&mut self, val: u8) -> Result<()> {
- self.write(val)?;
-
- Ok(())
- }
-
- pub fn write_u16(&mut self, val: u16) -> Result<()> {
- self.write((val >> 8) as u8)?;
- self.write((val & 0xFF) as u8)?;
-
- Ok(())
- }
-
- pub fn write_u32(&mut self, val: u32) -> Result<()> {
- self.write(((val >> 24) & 0xFF) as u8)?;
- self.write(((val >> 16) & 0xFF) as u8)?;
- self.write(((val >> 8) & 0xFF) as u8)?;
- self.write((val & 0xFF) as u8)?;
-
- Ok(())
- }
-
- pub fn write_qname(&mut self, qname: &str) -> Result<()> {
- for label in qname.split('.') {
- let len = label.len();
-
- self.write_u8(len as u8)?;
- for b in label.as_bytes() {
- self.write_u8(*b)?;
- }
- }
-
- if !qname.is_empty() {
- self.write_u8(0)?;
- }
-
- 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;
-
- Ok(())
- }
-
- pub fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> {
- self.set(pos, (val >> 8) as u8)?;
- self.set(pos + 1, (val & 0xFF) as u8)?;
-
- Ok(())
- }
-}
diff --git a/src/dns/packet/header.rs b/src/dns/packet/header.rs
deleted file mode 100644
index 2355ecb..0000000
--- a/src/dns/packet/header.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use super::{buffer::PacketBuffer, result::ResultCode};
-use crate::Result;
-
-#[derive(Clone, Debug)]
-pub struct DnsHeader {
- pub id: u16, // 16 bits
-
- pub recursion_desired: bool, // 1 bit
- pub truncated_message: bool, // 1 bit
- pub authoritative_answer: bool, // 1 bit
- pub opcode: u8, // 4 bits
- pub response: bool, // 1 bit
-
- pub rescode: ResultCode, // 4 bits
- pub checking_disabled: bool, // 1 bit
- pub authed_data: bool, // 1 bit
- pub z: bool, // 1 bit
- pub recursion_available: bool, // 1 bit
-
- pub questions: u16, // 16 bits
- pub answers: u16, // 16 bits
- pub authoritative_entries: u16, // 16 bits
- pub resource_entries: u16, // 16 bits
-}
-
-impl DnsHeader {
- pub fn new() -> Self {
- Self {
- id: 0,
-
- recursion_desired: false,
- truncated_message: false,
- authoritative_answer: false,
- opcode: 0,
- response: false,
-
- rescode: ResultCode::NOERROR,
- checking_disabled: false,
- authed_data: false,
- z: false,
- recursion_available: false,
-
- questions: 0,
- answers: 0,
- authoritative_entries: 0,
- resource_entries: 0,
- }
- }
-
- pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- self.id = buffer.read_u16()?;
- let flags = buffer.read_u16()?;
- let a = (flags >> 8) as u8;
- let b = (flags & 0xFF) as u8;
- self.recursion_desired = (a & (1 << 0)) > 0;
- self.truncated_message = (a & (1 << 1)) > 0;
- self.authoritative_answer = (a & (1 << 2)) > 0;
- self.opcode = (a >> 3) & 0x0F;
- self.response = (a & (1 << 7)) > 0;
-
- self.rescode = ResultCode::from_num(b & 0x0F);
- self.checking_disabled = (b & (1 << 4)) > 0;
- self.authed_data = (b & (1 << 5)) > 0;
- self.z = (b & (1 << 6)) > 0;
- self.recursion_available = (b & (1 << 7)) > 0;
-
- self.questions = buffer.read_u16()?;
- self.answers = buffer.read_u16()?;
- self.authoritative_entries = buffer.read_u16()?;
- self.resource_entries = buffer.read_u16()?;
-
- // Return the constant header size
- Ok(())
- }
-
- pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.write_u16(self.id)?;
-
- buffer.write_u8(
- (self.recursion_desired as u8)
- | ((self.truncated_message as u8) << 1)
- | ((self.authoritative_answer as u8) << 2)
- | (self.opcode << 3)
- | ((self.response as u8) << 7),
- )?;
-
- buffer.write_u8(
- (self.rescode as u8)
- | ((self.checking_disabled as u8) << 4)
- | ((self.authed_data as u8) << 5)
- | ((self.z as u8) << 6)
- | ((self.recursion_available as u8) << 7),
- )?;
-
- buffer.write_u16(self.questions)?;
- buffer.write_u16(self.answers)?;
- buffer.write_u16(self.authoritative_entries)?;
- buffer.write_u16(self.resource_entries)?;
-
- Ok(())
- }
-}
diff --git a/src/dns/packet/mod.rs b/src/dns/packet/mod.rs
deleted file mode 100644
index 9873b94..0000000
--- a/src/dns/packet/mod.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use std::net::IpAddr;
-
-use self::{
- buffer::PacketBuffer, header::DnsHeader, query::QueryType, question::DnsQuestion,
- record::DnsRecord,
-};
-use crate::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 {
- pub header: DnsHeader,
- pub questions: Vec,
- pub answers: Vec,
- pub authorities: Vec,
- pub resources: Vec,
-}
-
-impl Packet {
- pub fn new() -> Self {
- Self {
- header: DnsHeader::new(),
- questions: Vec::new(),
- answers: Vec::new(),
- authorities: Vec::new(),
- resources: Vec::new(),
- }
- }
-
- pub fn from_buffer(buffer: &mut PacketBuffer) -> Result {
- let mut result = Self::new();
- result.header.read(buffer)?;
-
- for _ in 0..result.header.questions {
- let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0));
- question.read(buffer)?;
- result.questions.push(question);
- }
-
- for _ in 0..result.header.answers {
- let rec = DnsRecord::read(buffer)?;
- result.answers.push(rec);
- }
- for _ in 0..result.header.authoritative_entries {
- let rec = DnsRecord::read(buffer)?;
- result.authorities.push(rec);
- }
- for _ in 0..result.header.resource_entries {
- let rec = DnsRecord::read(buffer)?;
- result.resources.push(rec);
- }
-
- Ok(result)
- }
-
- pub fn write(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- self.header.questions = self.questions.len() as u16;
- self.header.answers = self.answers.len() as u16;
- self.header.authoritative_entries = self.authorities.len() as u16;
- self.header.resource_entries = self.resources.len() as u16;
-
- self.header.write(buffer)?;
-
- for question in &self.questions {
- question.write(buffer)?;
- }
- for rec in &self.answers {
- rec.write(buffer)?;
- }
- for rec in &self.authorities {
- rec.write(buffer)?;
- }
- for rec in &self.resources {
- rec.write(buffer)?;
- }
-
- Ok(())
- }
-
- pub fn get_random_a(&self) -> Option {
- self.answers
- .iter()
- .filter_map(|record| match record {
- DnsRecord::A { addr, .. } => Some(IpAddr::V4(*addr)),
- DnsRecord::AAAA { addr, .. } => Some(IpAddr::V6(*addr)),
- _ => None,
- })
- .next()
- }
-
- fn get_ns<'a>(&'a self, qname: &'a str) -> impl Iterator- {
- self.authorities
- .iter()
- .filter_map(|record| match record {
- DnsRecord::NS { domain, host, .. } => Some((domain.as_str(), host.as_str())),
- _ => None,
- })
- .filter(move |(domain, _)| qname.ends_with(*domain))
- }
-
- pub fn get_resolved_ns(&self, qname: &str) -> Option {
- self.get_ns(qname)
- .flat_map(|(_, host)| {
- self.resources
- .iter()
- .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))
- }
- _ => None,
- })
- })
- .next()
- }
-
- pub fn get_unresolved_ns<'a>(&'a self, qname: &'a str) -> Option<&'a str> {
- self.get_ns(qname).map(|(_, host)| host).next()
- }
-}
diff --git a/src/dns/packet/query.rs b/src/dns/packet/query.rs
deleted file mode 100644
index 732b9b2..0000000
--- a/src/dns/packet/query.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
-pub enum QueryType {
- UNKNOWN(u16),
- A, // 1
- NS, // 2
- CNAME, // 5
- SOA, // 6
- PTR, // 12
- MX, // 15
- TXT, // 16
- AAAA, // 28
- SRV, // 33
- OPT, // 41
- CAA, // 257
- AR, // 1000
- AAAAR, // 1001
-}
-
-impl QueryType {
- pub fn to_num(&self) -> u16 {
- match *self {
- Self::UNKNOWN(x) => x,
- 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,
- Self::AR => 1000,
- Self::AAAAR => 1001,
- }
- }
-
- pub fn from_num(num: u16) -> Self {
- match num {
- 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,
- 1000 => Self::AR,
- 1001 => Self::AAAAR,
- _ => Self::UNKNOWN(num),
- }
- }
-
- pub fn allowed_actions(&self) -> (bool, bool) {
- // 0. duplicates allowed
- // 1. allowed to be created by database
- match self {
- QueryType::UNKNOWN(_) => (false, false),
- QueryType::A => (true, true),
- QueryType::NS => (false, true),
- QueryType::CNAME => (false, true),
- QueryType::SOA => (false, false),
- QueryType::PTR => (false, true),
- QueryType::MX => (false, true),
- QueryType::TXT => (true, true),
- QueryType::AAAA => (true, true),
- QueryType::SRV => (false, true),
- QueryType::OPT => (false, false),
- QueryType::CAA => (false, true),
- QueryType::AR => (false, true),
- QueryType::AAAAR => (false, true),
- }
- }
-}
diff --git a/src/dns/packet/question.rs b/src/dns/packet/question.rs
deleted file mode 100644
index 9042e1c..0000000
--- a/src/dns/packet/question.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use super::{buffer::PacketBuffer, query::QueryType, Result};
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct DnsQuestion {
- pub name: String,
- pub qtype: QueryType,
-}
-
-impl DnsQuestion {
- pub fn new(name: String, qtype: QueryType) -> Self {
- Self { name, qtype }
- }
-
- pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.read_qname(&mut self.name)?;
- self.qtype = QueryType::from_num(buffer.read_u16()?); // qtype
- let _ = buffer.read_u16()?; // class
-
- Ok(())
- }
-
- pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> {
- buffer.write_qname(&self.name)?;
-
- let typenum = self.qtype.to_num();
- buffer.write_u16(typenum)?;
- buffer.write_u16(1)?;
-
- Ok(())
- }
-}
diff --git a/src/dns/packet/record.rs b/src/dns/packet/record.rs
deleted file mode 100644
index 88008f0..0000000
--- a/src/dns/packet/record.rs
+++ /dev/null
@@ -1,544 +0,0 @@
-use std::net::{Ipv4Addr, Ipv6Addr};
-
-use rand::RngCore;
-use serde::{Deserialize, Serialize};
-use tracing::{trace, warn};
-
-use super::{buffer::PacketBuffer, query::QueryType, Result};
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
-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
- AR {
- domain: String,
- ttl: u32,
- },
- AAAAR {
- domain: String,
- ttl: u32,
- },
-}
-
-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()?;
-
- trace!("Reading DNS Record TYPE: {:?}", qtype);
-
- let header_pos = buffer.pos();
-
- 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::AR { ref domain, 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 mut rand = rand::thread_rng();
- buffer.write_u32(rand.next_u32())?;
- }
- Self::AAAAR { ref domain, 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 mut rand = rand::thread_rng();
- buffer.write_u32(rand.next_u32())?;
- buffer.write_u32(rand.next_u32())?;
- buffer.write_u32(rand.next_u32())?;
- buffer.write_u32(rand.next_u32())?;
- }
- Self::UNKNOWN { .. } => {
- warn!("Skipping record: {self:?}");
- }
- }
-
- Ok(buffer.pos() - start_pos)
- }
-
- pub fn get_domain(&self) -> String {
- self.get_shared_domain().0
- }
-
- pub fn get_qtype(&self) -> QueryType {
- self.get_shared_domain().1
- }
-
- pub fn get_ttl(&self) -> u32 {
- self.get_shared_domain().2
- }
-
- fn get_shared_domain(&self) -> (String, QueryType, u32) {
- match self {
- DnsRecord::UNKNOWN {
- domain, ttl, qtype, ..
- } => (domain.clone(), QueryType::UNKNOWN(*qtype), *ttl),
- DnsRecord::AAAA { domain, ttl, .. } => (domain.clone(), QueryType::AAAA, *ttl),
- DnsRecord::A { domain, ttl, .. } => (domain.clone(), QueryType::A, *ttl),
- DnsRecord::NS { domain, ttl, .. } => (domain.clone(), QueryType::NS, *ttl),
- DnsRecord::CNAME { domain, ttl, .. } => (domain.clone(), QueryType::CNAME, *ttl),
- DnsRecord::SOA { domain, ttl, .. } => (domain.clone(), QueryType::SOA, *ttl),
- DnsRecord::PTR { domain, ttl, .. } => (domain.clone(), QueryType::PTR, *ttl),
- DnsRecord::MX { domain, ttl, .. } => (domain.clone(), QueryType::MX, *ttl),
- DnsRecord::TXT { domain, ttl, .. } => (domain.clone(), QueryType::TXT, *ttl),
- DnsRecord::SRV { domain, ttl, .. } => (domain.clone(), QueryType::SRV, *ttl),
- DnsRecord::CAA { domain, ttl, .. } => (domain.clone(), QueryType::CAA, *ttl),
- DnsRecord::AR { domain, ttl, .. } => (domain.clone(), QueryType::AR, *ttl),
- DnsRecord::AAAAR { domain, ttl, .. } => (domain.clone(), QueryType::AAAAR, *ttl),
- }
- }
-}
diff --git a/src/dns/packet/result.rs b/src/dns/packet/result.rs
deleted file mode 100644
index 41c8ba9..0000000
--- a/src/dns/packet/result.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ResultCode {
- NOERROR = 0,
- FORMERR = 1,
- SERVFAIL = 2,
- NXDOMAIN = 3,
- NOTIMP = 4,
- REFUSED = 5,
-}
-
-impl ResultCode {
- pub fn from_num(num: u8) -> Self {
- match num {
- 1 => Self::FORMERR,
- 2 => Self::SERVFAIL,
- 3 => Self::NXDOMAIN,
- 4 => Self::NOTIMP,
- 5 => Self::REFUSED,
- 0 | _ => Self::NOERROR,
- }
- }
-}
diff --git a/src/dns/resolver.rs b/src/dns/resolver.rs
deleted file mode 100644
index 18b5bba..0000000
--- a/src/dns/resolver.rs
+++ /dev/null
@@ -1,230 +0,0 @@
-use super::binding::Connection;
-use super::packet::{query::QueryType, question::DnsQuestion, result::ResultCode, Packet};
-use crate::Result;
-use crate::{config::Config, database::Database, 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,
- database: Arc,
- cache: Cache,
-}
-
-impl Resolver {
- pub fn new(
- request_id: u16,
- connection: Connection,
- config: Arc,
- database: Arc,
- cache: Cache,
- ) -> Self {
- Self {
- request_id,
- connection,
- config,
- database,
- cache,
- }
- }
-
- async fn lookup_database(&self, question: &DnsQuestion) -> Option {
- let records = match self
- .database
- .get_records(&question.name, question.qtype)
- .await
- {
- Ok(record) => record,
- Err(err) => {
- error!("{err}");
- return None;
- }
- };
-
- if records.is_empty() {
- return None;
- }
-
- let mut packet = Packet::new();
-
- packet.header.id = self.request_id;
- packet.header.questions = 1;
- packet.header.answers = records.len() as u16;
- packet.header.recursion_desired = true;
- packet
- .questions
- .push(DnsQuestion::new(question.name.to_string(), question.qtype));
-
- for record in records {
- packet.answers.push(record);
- }
-
- trace!(
- "Found stored value for {:?} {}",
- question.qtype,
- question.name
- );
-
- Some(packet)
- }
-
- async fn lookup_cache(&self, question: &DnsQuestion) -> Option {
- 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 {:?} {}",
- question.qtype,
- question.name
- );
-
- Some(packet)
- }
-
- async fn lookup_fallback(&self, question: &DnsQuestion, 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(question.name.to_string(), question.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 fn lookup(&self, question: &DnsQuestion, server: (IpAddr, u16)) -> Packet {
- if let Some(packet) = self.lookup_cache(question).await {
- return packet;
- };
-
- if let Some(packet) = self.lookup_database(question).await {
- return packet;
- };
-
- trace!(
- "Attempting lookup of {:?} {} with ns {}",
- question.qtype,
- question.name,
- server.0
- );
-
- self.lookup_fallback(question, server).await
- }
-
- #[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.dns_fallback.clone();
-
- loop {
- let ns_copy = ns;
-
- let server = (ns_copy, 53);
- let response = self.lookup(&question, 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/dns/server.rs b/src/dns/server.rs
deleted file mode 100644
index 65d15df..0000000
--- a/src/dns/server.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use super::{
- binding::Binding,
- packet::{question::DnsQuestion, Packet},
- resolver::Resolver,
-};
-use crate::{config::Config, database::Database, Result};
-use moka::future::Cache;
-use std::{net::SocketAddr, sync::Arc, time::Duration};
-use tokio::task::JoinHandle;
-use tracing::{error, info};
-
-pub struct DnsServer {
- addr: SocketAddr,
- config: Arc,
- database: Arc,
- cache: Cache,
-}
-
-impl DnsServer {
- pub async fn new(config: Config, database: Database) -> Result {
- let addr = format!("[::]:{}", config.dns_port).parse::()?;
- let cache = Cache::builder()
- .time_to_live(Duration::from_secs(60 * 60))
- .max_capacity(config.dns_cache_size)
- .build();
-
- info!("Created DNS cache with size of {}", config.dns_cache_size);
-
- Ok(Self {
- addr,
- config: Arc::new(config),
- database: Arc::new(database),
- cache,
- })
- }
-
- pub async fn run(&self) -> Result<(JoinHandle<()>, JoinHandle<()>)> {
- 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.dns_fallback
- );
- info!(
- "Listening for TCP and UDP traffic on [::]:{}",
- self.config.dns_port
- );
-
- Ok((udp_handle, tcp_handle))
- }
-
- fn listen(&self, mut binding: Binding) -> JoinHandle<()> {
- let config = self.config.clone();
- let database = self.database.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(),
- database.clone(),
- cache.clone(),
- );
-
- let name = binding.name().to_string();
- tokio::spawn(async move {
- if let Err(err) = resolver.handle_query().await {
- error!("{} request {} failed: {:?}", name, id, err);
- };
- });
-
- id += 1;
- }
- })
- }
-}
diff --git a/src/io/log.c b/src/io/log.c
new file mode 100644
index 0000000..ddf56ff
--- /dev/null
+++ b/src/io/log.c
@@ -0,0 +1,49 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "log.h"
+
+#ifdef LOG
+
+void logmsg(LogLevel level, const char* msg, ...) {
+
+ INIT_LOG_BOUNDS
+ INIT_LOG_BUFFER(buffer)
+
+ time_t now = time(NULL);
+ struct tm *tm = localtime(&now);
+ APPEND(buffer, "\x1b[97m%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ switch (level) {
+ case DEBUG:
+ APPEND(buffer, "\x1b[%dm%s\x1b[97m ", 95, "DEBUG");
+ break;
+ case TRACE:
+ APPEND(buffer, "\x1b[%dm%s\x1b[97m ", 96, "TRACE");
+ break;
+ case INFO:
+ APPEND(buffer, "\x1b[%dm%s\x1b[97m ", 92, "INFO");
+ break;
+ case WARN:
+ APPEND(buffer, "\x1b[%dm%s\x1b[97m ", 93, "WARN");
+ break;
+ case ERROR:
+ APPEND(buffer, "\x1b[%dm%s\x1b[97m ", 91, "ERROR");
+ break;
+ break;
+ }
+
+ va_list valist;
+ va_start(valist, msg);
+ t += vsnprintf(buffer + t, BUF_LENGTH - t, msg, valist);
+ va_end(valist);
+
+ APPEND(buffer, "\n");
+
+ fwrite(&buffer, t, 1, stdout);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/io/log.h b/src/io/log.h
new file mode 100644
index 0000000..c2fbd90
--- /dev/null
+++ b/src/io/log.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include
+
+#define LOG
+#ifdef LOG
+
+typedef enum {
+ DEBUG,
+ TRACE,
+ INFO,
+ WARN,
+ ERROR,
+} LogLevel;
+
+#define BUF_LENGTH 256
+#define INIT_LOG_BUFFER(name) char name[BUF_LENGTH];
+#define INIT_LOG_BOUNDS int t = 0;
+#define APPEND(buffer, msg, ...) t += snprintf(buffer + t, BUF_LENGTH - t, msg, ##__VA_ARGS__);
+#define LOGONLY(code) code
+
+void logmsg(LogLevel level, const char* msg, ...)
+ __attribute__ ((__format__(printf, 2, 3)));
+
+#define DEBUG(msg, ...) logmsg(DEBUG, msg, ##__VA_ARGS__)
+#define TRACE(msg, ...) logmsg(TRACE, msg, ##__VA_ARGS__)
+#define INFO(msg, ...) logmsg(INFO, msg, ##__VA_ARGS__)
+#define WARN(msg, ...) logmsg(WARN, msg, ##__VA_ARGS__)
+#define ERROR(msg, ...) logmsg(ERROR, msg, ##__VA_ARGS__)
+
+#else
+
+#define BUF_LENGTH
+#define INIT_LOG_BUFFER(name)
+#define INIT_LOG_BOUNDS
+#define APPEND(buffer, msg, ...)
+#define LOGONLY(code)
+
+#define DEBUG(msg, ...)
+#define TRACE(msg, ...)
+#define INFO(msg, ...)
+#define WARN(msg, ...)
+#define ERROR(msg, ...)
+
+#endif
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..13dae57
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,32 @@
+#include "server/server.h"
+
+#include
+#include
+
+#define DEFAULT_PORT 53
+
+static uint16_t get_port(const char* port_str) {
+ if (port_str == NULL) {
+ return DEFAULT_PORT;
+ }
+
+ uint16_t port;
+ if ((port = strtoul(port_str, NULL, 10)) == 0) {
+ return DEFAULT_PORT;
+ }
+
+ return port;
+}
+
+int main(void) {
+
+ const char* port_str = getenv("PORT");
+ uint16_t port = get_port(port_str);
+
+ Server server;
+ server_init(port, &server);
+ server_run(&server);
+ server_free(&server);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 679e87b..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use std::time::{SystemTime, UNIX_EPOCH};
-
-use config::Config;
-
-use database::Database;
-use dotenv::dotenv;
-use dns::server::DnsServer;
-use tracing::{error, metadata::LevelFilter};
-use tracing_subscriber::{
- filter::filter_fn, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer,
-};
-use web::WebServer;
-
-mod config;
-mod database;
-mod dns;
-mod web;
-
-type Error = Box;
-pub type Result = std::result::Result;
-
-#[tokio::main]
-async fn main() {
- if let Err(err) = run().await {
- error!("{err}")
- };
-}
-
-async fn run() -> Result<()> {
- dotenv().ok();
-
- tracing_subscriber::registry()
- .with(
- tracing_subscriber::fmt::layer()
- .with_filter(LevelFilter::TRACE)
- .with_filter(filter_fn(|metadata| {
- metadata.target().starts_with("wrapper")
- })),
- )
- .init();
-
- let config = Config::new();
- let database = Database::new(config.clone()).await?;
-
- let dns_server = DnsServer::new(config.clone(), database.clone()).await?;
- let (udp, tcp) = dns_server.run().await?;
-
- let web_server = WebServer::new(config, database).await?;
- let web = web_server.run().await?;
-
- tokio::join!(udp).0?;
- tokio::join!(tcp).0?;
- tokio::join!(web).0?;
-
- Ok(())
-}
-
-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/src/packet/buffer.c b/src/packet/buffer.c
new file mode 100644
index 0000000..28dd73b
--- /dev/null
+++ b/src/packet/buffer.c
@@ -0,0 +1,240 @@
+#include "buffer.h"
+
+#include
+#include
+#include
+
+struct PacketBuffer {
+ uint8_t* arr;
+ int capacity;
+ int index;
+ int size;
+};
+
+PacketBuffer* buffer_create(int capacity) {
+ PacketBuffer* buffer = malloc(sizeof(PacketBuffer));
+ buffer->arr = malloc(capacity);
+ buffer->capacity = capacity;
+ buffer->size = 0;
+ buffer->index = 0;
+ return buffer;
+}
+
+void buffer_free(PacketBuffer* buffer) {
+ free(buffer->arr);
+ free(buffer);
+}
+
+void buffer_seek(PacketBuffer* buffer, int index) {
+ buffer->index = index;
+}
+
+uint8_t buffer_read(PacketBuffer* buffer) {
+ if (buffer->index > buffer->size) {
+ return 0;
+ }
+ uint8_t data = buffer->arr[buffer->index];
+ buffer->index++;
+ return data;
+}
+
+uint16_t buffer_read_short(PacketBuffer* buffer) {
+ return
+ (uint16_t) buffer_read(buffer) << 8 |
+ (uint16_t) buffer_read(buffer);
+}
+
+uint32_t buffer_read_int(PacketBuffer* buffer) {
+ return
+ (uint32_t) buffer_read(buffer) << 24 |
+ (uint32_t) buffer_read(buffer) << 16 |
+ (uint32_t) buffer_read(buffer) << 8 |
+ (uint32_t) buffer_read(buffer);
+}
+
+uint8_t buffer_get(PacketBuffer* buffer, int index) {
+ if (index > buffer->size) {
+ return 0;
+ }
+ uint8_t data = buffer->arr[index];
+ return data;
+}
+
+uint8_t* buffer_get_range(PacketBuffer* buffer, int start, int len) {
+ uint8_t* arr = malloc(len);
+ for (int i = 0; i < len; i++) {
+ arr[i] = buffer_get(buffer, start + i);
+ }
+ return arr;
+}
+
+uint16_t buffer_get_size(PacketBuffer* buffer) {
+ return (uint16_t) buffer->size + 1;
+}
+
+static void write(uint8_t** buffer, uint8_t* size, uint8_t* capacity, uint8_t data) {
+ if (*size == *capacity) {
+ *capacity *= 2;
+ *buffer = realloc(*buffer, *capacity);
+ }
+ (*buffer)[*size] = data;
+ (*size)++;
+}
+
+void buffer_read_qname(PacketBuffer* buffer, uint8_t** out) {
+ int index = buffer->index;
+ int jumped = 0;
+
+ int max_jumps = 5;
+ int jumps_performed = 0;
+
+ uint8_t length = 0;
+ uint8_t capacity = 8;
+ *out = malloc(capacity);
+ write(out, &length, &capacity, 0);
+
+ while(1) {
+ if (jumps_performed > max_jumps) {
+ break;
+ }
+
+ uint8_t len = buffer_get(buffer, index);
+
+ if ((len & 0xC0) == 0xC0) {
+ if (jumped == 0) {
+ buffer_seek(buffer, index + 2);
+ }
+
+ uint16_t b2 = (uint16_t) buffer_get(buffer, index + 1);
+ uint16_t offset = ((((uint16_t) len) ^ 0xC0) << 8) | b2;
+ index = (int) offset;
+ jumped = 1;
+ jumps_performed++;
+ continue;
+ }
+
+ index++;
+
+ if (len == 0) {
+ break;
+ }
+
+ if (length > 1) {
+ write(out, &length, &capacity, '.');
+ }
+
+ uint8_t* range = buffer_get_range(buffer, index, len);
+ for (uint8_t i = 0; i < len; i++) {
+ write(out, &length, &capacity, range[i]);
+ }
+ free(range);
+
+ index += (int) len;
+ }
+
+ if (jumped == 0) {
+ buffer_seek(buffer, index);
+ }
+
+ (*out)[0] = length - 1;
+}
+
+void buffer_read_string(PacketBuffer* buffer, uint8_t** out) {
+ uint8_t len = buffer_read(buffer);
+ buffer_read_n(buffer, out, len);
+}
+
+void buffer_read_n(PacketBuffer* buffer, uint8_t** out, uint8_t len) {
+ *out = malloc(len + 1);
+ *out[0] = len;
+ memcpy(*out + 1, buffer->arr + buffer->index, len);
+ buffer->index += len;
+}
+
+void buffer_write(PacketBuffer* buffer, uint8_t data) {
+ if(buffer->index == buffer->capacity) {
+ buffer->capacity *= 2;
+ buffer->arr = realloc(buffer->arr, buffer->capacity);
+ }
+ if (buffer->size < buffer->index) {
+ buffer->size = buffer->index;
+ }
+ buffer->arr[buffer->index] = data;
+ buffer->index++;
+}
+
+void buffer_write_short(PacketBuffer* buffer, uint16_t data) {
+ buffer_write(buffer, (uint8_t)(data >> 8));
+ buffer_write(buffer, (uint8_t)(data & 0xFF));
+}
+
+void buffer_write_int(PacketBuffer* buffer, uint32_t data) {
+ buffer_write(buffer, (uint8_t)(data >> 24));
+ buffer_write(buffer, (uint8_t)(data >> 16));
+ buffer_write(buffer, (uint8_t)(data >> 8));
+ buffer_write(buffer, (uint8_t)(data & 0xFF));
+}
+
+void buffer_write_qname(PacketBuffer* buffer, uint8_t* in) {
+ uint8_t part = 0;
+ uint8_t len = in[0];
+
+ buffer_write(buffer, 0);
+
+ if (len == 0) {
+ return;
+ }
+
+ for(uint8_t i = 0; i < len; i ++) {
+ if (in[i+1] == '.') {
+ buffer_set(buffer, part, buffer->index - (int)part - 1);
+ buffer_write(buffer, 0);
+ part = 0;
+ } else {
+ buffer_write(buffer, in[i+1]);
+ part++;
+ }
+ }
+ buffer_set(buffer, part, buffer->index - (int)part - 1);
+ buffer_write(buffer, 0);
+}
+
+void buffer_write_string(PacketBuffer* buffer, uint8_t* in) {
+ buffer_write(buffer, in[0]);
+ buffer_write_n(buffer, in + 1, in[0]);
+}
+
+void buffer_write_n(PacketBuffer* buffer, uint8_t* in, int len) {
+ if (buffer->size + len >= buffer->capacity) {
+ buffer->capacity *= 2;
+ buffer->capacity += len;
+ buffer->arr = realloc(buffer->arr, buffer->capacity);
+ }
+ memcpy(buffer->arr + buffer->index, in, len);
+ buffer->size += len;
+ buffer->index += len;
+}
+
+void buffer_set(PacketBuffer* buffer, uint8_t data, int index) {
+ if (index > buffer->size) {
+ return;
+ }
+ buffer->arr[index] = data;
+}
+
+void buffer_set_uint16_t(PacketBuffer* buffer, uint16_t data, int index) {
+ buffer_set(buffer, (uint8_t)(data >> 8), index);
+ buffer_set(buffer, (uint8_t)(data & 0xFF), index + 1);
+}
+
+int buffer_get_index(PacketBuffer* buffer) {
+ return buffer->index;
+}
+
+void buffer_step(PacketBuffer* buffer, int len) {
+ buffer->index += len;
+}
+
+uint8_t* buffer_get_ptr(PacketBuffer* buffer) {
+ return buffer->arr;
+}
diff --git a/src/packet/buffer.h b/src/packet/buffer.h
new file mode 100644
index 0000000..ad3145d
--- /dev/null
+++ b/src/packet/buffer.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include
+
+typedef struct PacketBuffer PacketBuffer;
+
+PacketBuffer* buffer_create(int capacity);
+
+void buffer_free(PacketBuffer* buffer);
+
+void buffer_seek(PacketBuffer* buffer, int index);
+
+uint8_t buffer_read(PacketBuffer* buffer);
+
+uint16_t buffer_read_short(PacketBuffer* buffer);
+
+uint32_t buffer_read_int(PacketBuffer* buffer);
+
+uint8_t buffer_get(PacketBuffer* buffer, int index);
+
+uint8_t* buffer_get_range(PacketBuffer* buffer, int start, int len);
+
+uint16_t buffer_get_size(PacketBuffer* buffer);
+
+void buffer_read_qname(PacketBuffer* buffer, uint8_t** out);
+
+void buffer_read_string(PacketBuffer* buffer, uint8_t** out);
+
+void buffer_read_n(PacketBuffer* buffer, uint8_t** out, uint8_t len);
+
+void buffer_write(PacketBuffer* buffer, uint8_t data);
+
+void buffer_write_short(PacketBuffer* buffer, uint16_t data);
+
+void buffer_write_int(PacketBuffer* buffer, uint32_t data);
+
+void buffer_write_qname(PacketBuffer* buffer, uint8_t* in);
+
+void buffer_write_string(PacketBuffer* buffer, uint8_t* in);
+
+void buffer_write_n(PacketBuffer* buffer, uint8_t* in, int len);
+
+void buffer_set(PacketBuffer* buffer, uint8_t data, int index);
+
+void buffer_set_uint16_t(PacketBuffer* buffer, uint16_t data, int index);
+
+int buffer_get_index(PacketBuffer* buffer);
+
+void buffer_step(PacketBuffer* buffer, int len);
+
+uint8_t* buffer_get_ptr(PacketBuffer* buffer);
diff --git a/src/packet/header.c b/src/packet/header.c
new file mode 100644
index 0000000..fd601ce
--- /dev/null
+++ b/src/packet/header.c
@@ -0,0 +1,93 @@
+#include "header.h"
+#include "buffer.h"
+
+#include
+#include
+#include
+
+uint8_t rescode_to_id(ResultCode code) {
+ switch(code) {
+ case NOERROR:
+ return 0;
+ case FORMERR:
+ return 1;
+ case SERVFAIL:
+ return 2;
+ case NXDOMAIN:
+ return 3;
+ case NOTIMP:
+ return 4;
+ case REFUSED:
+ return 5;
+ default:
+ return 2;
+ }
+}
+
+ResultCode rescode_from_id(uint8_t id) {
+ switch(id) {
+ case 0:
+ return NOERROR;
+ case 1:
+ return FORMERR;
+ case 2:
+ return SERVFAIL;
+ case 3:
+ return NXDOMAIN;
+ case 4:
+ return NOTIMP;
+ case 5:
+ return REFUSED;
+ default:
+ return FORMERR;
+ }
+}
+
+void read_header(PacketBuffer* buffer, Header* header) {
+ // memset(header, 0, sizeof(Header));
+ header->id = buffer_read_short(buffer);
+
+ uint8_t a = buffer_read(buffer);
+ header->recursion_desired = (a & (1 << 0)) > 0;
+ header->truncated_message = (a & (1 << 1)) > 0;
+ header->authorative_answer = (a & (1 << 2)) > 0;
+ header->opcode = (a >> 3) & 0x0F;
+ header->response = (a & (1 << 7)) > 0;
+
+ uint8_t b = buffer_read(buffer);
+ header->rescode = rescode_from_id(b & 0x0F);
+ header->checking_disabled = (b & (1 << 4)) > 0;
+ header->authed_data = (b& (1 << 4)) > 0;
+ header->z = (b & (1 << 6)) > 0;
+ header->recursion_available = (b & (1 << 7)) > 0;
+
+ header->questions = buffer_read_short(buffer);
+ header->answers = buffer_read_short(buffer);
+ header->authoritative_entries = buffer_read_short(buffer);
+ header->resource_entries = buffer_read_short(buffer);
+}
+
+void write_header(PacketBuffer* buffer, Header* header) {
+ buffer_write_short(buffer, header->id);
+
+ buffer_write(buffer,
+ ((uint8_t) header->recursion_desired) |
+ ((uint8_t) header->truncated_message << 1) |
+ ((uint8_t) header->authorative_answer << 2) |
+ (header->opcode << 3) |
+ ((uint8_t) header->response << 7)
+ );
+
+ buffer_write(buffer,
+ (rescode_to_id(header->rescode)) |
+ ((uint8_t) header->checking_disabled << 4) |
+ ((uint8_t) header->authed_data << 5) |
+ ((uint8_t) header->z << 6) |
+ ((uint8_t) header->recursion_available << 7)
+ );
+
+ buffer_write_short(buffer, header->questions);
+ buffer_write_short(buffer, header->answers);
+ buffer_write_short(buffer, header->authoritative_entries);
+ buffer_write_short(buffer, header->resource_entries);
+}
diff --git a/src/packet/header.h b/src/packet/header.h
new file mode 100644
index 0000000..d9a8cea
--- /dev/null
+++ b/src/packet/header.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "buffer.h"
+
+#include
+
+typedef enum {
+ NOERROR, // 0
+ FORMERR, // 1
+ SERVFAIL, // 2
+ NXDOMAIN, // 3,
+ NOTIMP, // 4
+ REFUSED, // 5
+} ResultCode;
+
+uint8_t rescode_to_id(ResultCode code);
+ResultCode rescode_from_id(uint8_t id);
+
+typedef struct {
+ uint16_t id;
+
+ bool recursion_desired; // 1 bit
+ bool truncated_message; // 1 bit
+ bool authorative_answer; // 1 bit
+ uint8_t opcode; // 4 bits
+ bool response; // 1 bit
+
+ ResultCode rescode; // 4 bits
+ bool checking_disabled; // 1 bit
+ bool authed_data; // 1 bit
+ bool z; // 1 bit
+ bool recursion_available; // 1 bit
+
+ uint16_t questions; // 16 bits
+ uint16_t answers; // 16 bits
+ uint16_t authoritative_entries; // 16 bits
+ uint16_t resource_entries; // 16 bits
+} Header;
+
+void read_header(PacketBuffer* buffer, Header* header);
+void write_header(PacketBuffer* buffer, Header* header);
diff --git a/src/packet/packet.c b/src/packet/packet.c
new file mode 100644
index 0000000..9b1159d
--- /dev/null
+++ b/src/packet/packet.c
@@ -0,0 +1,171 @@
+#include "packet.h"
+#include "buffer.h"
+#include "header.h"
+#include "question.h"
+#include "record.h"
+
+#include
+#include
+#include
+
+void read_packet(PacketBuffer* buffer, Packet* packet) {
+ read_header(buffer, &packet->header);
+
+ packet->questions = malloc(sizeof(Question) * packet->header.questions);
+ for(uint16_t i = 0; i < packet->header.questions; i++) {
+ read_question(buffer, &packet->questions[i]);
+ }
+
+ packet->answers = malloc(sizeof(Record) * packet->header.answers);
+ for(uint16_t i = 0; i < packet->header.answers; i++) {
+ read_record(buffer, &packet->answers[i]);
+ }
+
+ packet->authorities = malloc(sizeof(Record) * packet->header.authoritative_entries);
+ for(uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ read_record(buffer, &packet->authorities[i]);
+ }
+
+ packet->resources = malloc(sizeof(Record) * packet->header.resource_entries);
+ for(uint16_t i = 0; i < packet->header.resource_entries; i++) {
+ read_record(buffer, &packet->resources[i]);
+ }
+}
+
+void write_packet(PacketBuffer* buffer, Packet* packet) {
+ write_header(buffer, &packet->header);
+
+ for(uint16_t i = 0; i < packet->header.questions; i++) {
+ write_question(buffer, &packet->questions[i]);
+ }
+
+ for(uint16_t i = 0; i < packet->header.answers; i++) {
+ write_record(buffer, &packet->answers[i]);
+ }
+
+ for(uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ write_record(buffer, &packet->authorities[i]);
+ }
+
+ for(uint16_t i = 0; i < packet->header.resource_entries; i++) {
+ write_record(buffer, &packet->resources[i]);
+ }
+}
+
+void free_packet(Packet* packet) {
+
+ for(uint16_t i = 0; i < packet->header.questions; i++) {
+ free_question(&packet->questions[i]);
+ }
+ free(packet->questions);
+
+ for(uint16_t i = 0; i < packet->header.answers; i++) {
+ free_record(&packet->answers[i]);
+ }
+ free(packet->answers);
+
+ for(uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ free_record(&packet->authorities[i]);
+ }
+ free(packet->authorities);
+
+ for(uint16_t i = 0; i < packet->header.resource_entries; i++) {
+ free_record(&packet->resources[i]);
+ }
+ free(packet->resources);
+}
+
+bool get_random_a(Packet* packet, IpAddr* addr) {
+ for (uint16_t i = 0; i < packet->header.answers; i++) {
+ Record record = packet->answers[i];
+ if (record.type == A) {
+ create_ip_addr((char*) &record.data.a.addr, addr);
+ return true;
+ } else if (record.type == AAAA) {
+ create_ip_addr6((char*) &record.data.aaaa.addr, addr);
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool ends_with(uint8_t* full, uint8_t* end) {
+ uint8_t check = end[0];
+ uint8_t len = full[0];
+
+ if (check > len) {
+ return false;
+ }
+
+ for(uint8_t i = 0; i < check; i++) {
+ if (end[check - 1 - i] != full[len - 1 - i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool equals(uint8_t* a, uint8_t* b) {
+ if (a[0] != b[0]) {
+ return false;
+ }
+
+ for(uint8_t i = 1; i < a[0] + 1; i++) {
+ if(a[i] != b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool get_resolved_ns(Packet* packet, uint8_t* qname, IpAddr* addr) {
+ for (uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ Record record = packet->authorities[i];
+ if (record.type != NS) {
+ continue;
+ }
+
+ if(!ends_with(qname, record.domain)) {
+ continue;
+ }
+
+ for (uint16_t i = 0; i < packet->header.resource_entries; i++) {
+ Record resource = packet->resources[i];
+ if (!equals(record.data.ns.host, resource.domain)) {
+ continue;
+ }
+
+ if (resource.type == A) {
+ create_ip_addr((char*) &record.data.a.addr, addr);
+ return true;
+ } else if (resource.type == AAAA) {
+ create_ip_addr6((char*) &record.data.aaaa.addr, addr);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool get_unresoled_ns(Packet* packet, uint8_t* qname, Question* question) {
+ for (uint16_t i = 0; i < packet->header.authoritative_entries; i++) {
+ Record record = packet->authorities[i];
+ if (record.type != NS) {
+ continue;
+ }
+
+ if(!ends_with(qname, record.domain)) {
+ continue;
+ }
+
+ uint8_t* host = record.data.ns.host;
+
+ question->qtype = NS;
+ question->domain = malloc(host[0] + 1);
+ memcpy(question->domain, host, host[0] + 1);
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/src/packet/packet.h b/src/packet/packet.h
new file mode 100644
index 0000000..aa1c35e
--- /dev/null
+++ b/src/packet/packet.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "buffer.h"
+#include "question.h"
+#include "header.h"
+#include "record.h"
+#include "../server/addr.h"
+
+typedef struct {
+ Header header;
+ Question* questions;
+ Record* answers;
+ Record* authorities;
+ Record* resources;
+} Packet;
+
+void read_packet(PacketBuffer* buffer, Packet* packet);
+void write_packet(PacketBuffer* buffer, Packet* packet);
+void free_packet(Packet* packet);
+
+bool get_random_a(Packet* packet, IpAddr* addr);
+bool get_resolved_ns(Packet* packet, uint8_t* qname, IpAddr* addr);
+bool get_unresoled_ns(Packet* packet, uint8_t* qname, Question* question);
diff --git a/src/packet/question.c b/src/packet/question.c
new file mode 100644
index 0000000..c2807d0
--- /dev/null
+++ b/src/packet/question.c
@@ -0,0 +1,94 @@
+#include
+#include
+
+#include "question.h"
+#include "buffer.h"
+#include "record.h"
+#include "../io/log.h"
+
+void read_question(PacketBuffer* buffer, Question* question) {
+ buffer_read_qname(buffer, &question->domain);
+
+ uint16_t qtype_num = buffer_read_short(buffer);
+ record_from_id(qtype_num, &question->qtype);
+ question->cls = buffer_read_short(buffer);
+
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_question(question, log);)
+ TRACE("Reading question: %s", log);
+}
+
+void write_question(PacketBuffer* buffer, Question* question) {
+ buffer_write_qname(buffer, question->domain);
+
+ uint16_t id = record_to_id(question->qtype);
+ buffer_write_short(buffer, id);
+
+ buffer_write_short(buffer, question->cls);
+
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_question(question, log);)
+ TRACE("Writing question: %s", log);
+}
+
+void free_question(Question* question) {
+ free(question->domain);
+}
+
+void print_question(Question* question, char* buffer) {
+ INIT_LOG_BOUNDS
+ switch (question->cls) {
+ case 1:
+ APPEND(buffer, "IN ");;
+ break;
+ case 3:
+ APPEND(buffer, "CH ");
+ break;
+ case 4:
+ APPEND(buffer, "HS ");
+ break;
+ default:
+ APPEND(buffer, "?? ");
+ break;
+ }
+ switch(question->qtype) {
+ case UNKOWN:
+ APPEND(buffer, "UNKOWN ");
+ break;
+ case A:
+ APPEND(buffer, "A ");
+ break;
+ case NS:
+ APPEND(buffer, "NS ");
+ break;
+ case CNAME:
+ APPEND(buffer, "CNAME ");
+ break;
+ case SOA:
+ APPEND(buffer, "SOA ");
+ break;
+ case PTR:
+ APPEND(buffer, "PTR ");
+ break;
+ case MX:
+ APPEND(buffer, "MX ");
+ break;
+ case TXT:
+ APPEND(buffer, "TXT ");
+ break;
+ case AAAA:
+ APPEND(buffer, "AAAA ");
+ break;
+ case SRV:
+ APPEND(buffer, "SRV ");
+ break;
+ case CAA:
+ APPEND(buffer, "CAA ");
+ break;
+ break;
+ }
+ APPEND(buffer, "%.*s",
+ question->domain[0],
+ question->domain + 1
+ );
+}
\ No newline at end of file
diff --git a/src/packet/question.h b/src/packet/question.h
new file mode 100644
index 0000000..e8c385a
--- /dev/null
+++ b/src/packet/question.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "buffer.h"
+#include "record.h"
+
+typedef struct {
+ uint8_t* domain;
+ RecordType qtype;
+ uint16_t cls;
+} Question;
+
+void read_question(PacketBuffer* buffer, Question* question);
+void write_question(PacketBuffer* buffer, Question* question);
+void free_question(Question* question);
+void print_question(Question* question, char* buffer);
diff --git a/src/packet/record.c b/src/packet/record.c
new file mode 100644
index 0000000..29c3bf0
--- /dev/null
+++ b/src/packet/record.c
@@ -0,0 +1,540 @@
+#include
+#include
+#include
+
+#include "record.h"
+#include "buffer.h"
+#include "../io/log.h"
+
+uint16_t record_to_id(RecordType type) {
+ switch (type) {
+ case A:
+ return 1;
+ case NS:
+ return 2;
+ case CNAME:
+ return 5;
+ case SOA:
+ return 6;
+ case PTR:
+ return 12;
+ case MX:
+ return 15;
+ case TXT:
+ return 16;
+ case AAAA:
+ return 28;
+ case SRV:
+ return 33;
+ case CAA:
+ return 257;
+ default:
+ return 0;
+ }
+}
+
+void record_from_id(uint16_t i, RecordType* type) {
+ switch (i) {
+ case 1:
+ *type = A;
+ break;
+ case 2:
+ *type = NS;
+ break;
+ case 5:
+ *type = CNAME;
+ break;
+ case 6:
+ *type = SOA;
+ break;
+ case 12:
+ *type = PTR;
+ break;
+ case 15:
+ *type = MX;
+ break;
+ case 16:
+ *type = TXT;
+ break;
+ case 28:
+ *type = AAAA;
+ break;
+ case 33:
+ *type = SRV;
+ break;
+ case 257:
+ *type = CAA;
+ break;
+ default:
+ *type = UNKOWN;
+ }
+}
+
+static void read_a_record(PacketBuffer* buffer, Record* record) {
+ ARecord data;
+ data.addr[0] = buffer_read(buffer);
+ data.addr[1] = buffer_read(buffer);
+ data.addr[2] = buffer_read(buffer);
+ data.addr[3] = buffer_read(buffer);
+
+ record->data.a = data;
+}
+
+static void read_ns_record(PacketBuffer* buffer, Record* record) {
+ NSRecord data;
+ buffer_read_qname(buffer, &data.host);
+
+ record->data.ns = data;
+}
+
+static void read_cname_record(PacketBuffer* buffer, Record* record) {
+ CNAMERecord data;
+ buffer_read_qname(buffer, &data.host);
+
+ record->data.cname = data;
+}
+
+static void read_soa_record(PacketBuffer* buffer, Record* record) {
+ SOARecord data;
+ buffer_read_qname(buffer, &data.mname);
+ buffer_read_qname(buffer, &data.nname);
+ data.serial = buffer_read_int(buffer);
+ data.refresh = buffer_read_int(buffer);
+ data.retry = buffer_read_int(buffer);
+ data.expire = buffer_read_int(buffer);
+ data.minimum = buffer_read_int(buffer);
+
+ record->data.soa = data;
+}
+
+static void read_ptr_record(PacketBuffer* buffer, Record* record) {
+ PTRRecord data;
+ buffer_read_qname(buffer, &data.pointer);
+
+ record->data.ptr = data;
+}
+
+static void read_mx_record(PacketBuffer* buffer, Record* record) {
+ MXRecord data;
+ data.priority = buffer_read_short(buffer);
+ buffer_read_qname(buffer, &data.host);
+
+ record->data.mx = data;
+}
+
+static void read_txt_record(PacketBuffer* buffer, Record* record) {
+ TXTRecord data;
+ data.len = 0;
+ data.text = malloc(sizeof(uint8_t*) * 2);
+
+ uint8_t capacity = 2;
+ while (1) {
+ if (data.len >= capacity) {
+ capacity *= 2;
+ data.text = realloc(data.text, sizeof(uint8_t*) * capacity);
+ }
+
+ buffer_read_string(buffer, &data.text[data.len]);
+ if(data.text[data.len][0] == 0) break;
+ data.len++;
+ }
+
+ record->data.txt = data;
+}
+
+static void read_aaaa_record(PacketBuffer* buffer, Record* record) {
+ AAAARecord data;
+ for (int i = 0; i < 16; i++) {
+ data.addr[i] = buffer_read(buffer);
+ }
+
+ record->data.aaaa = data;
+}
+
+static void read_srv_record(PacketBuffer* buffer, Record* record) {
+ SRVRecord data;
+ data.priority = buffer_read_short(buffer);
+ data.weight = buffer_read_short(buffer);
+ data.port = buffer_read_short(buffer);
+ buffer_read_qname(buffer, &data.target);
+
+ record->data.srv = data;
+}
+
+static void read_caa_record(PacketBuffer* buffer, Record* record, int header_pos) {
+ CAARecord data;
+ data.flags = buffer_read(buffer);
+ data.length = buffer_read(buffer);
+ buffer_read_n(buffer, &data.tag, data.length);
+ int value_len = ((int)record->len) + header_pos - buffer_get_index(buffer);
+ buffer_read_n(buffer, &data.value, (uint8_t)value_len);
+
+ record->data.caa = data;
+}
+
+void read_record(PacketBuffer* buffer, Record* record) {
+ buffer_read_qname(buffer, &record->domain);
+
+ uint16_t qtype_num = buffer_read_short(buffer);
+ record_from_id(qtype_num, &record->type);
+
+ record->cls = buffer_read_short(buffer);
+ record->ttl = buffer_read_int(buffer);
+ record->len = buffer_read_short(buffer);
+
+ int header_pos = buffer_get_index(buffer);
+
+ switch (record->type) {
+ case A:
+ read_a_record(buffer, record);
+ break;
+ case NS:
+ read_ns_record(buffer, record);
+ break;
+ case CNAME:
+ read_cname_record(buffer, record);
+ break;
+ case SOA:
+ read_soa_record(buffer, record);
+ break;
+ case PTR:
+ read_ptr_record(buffer, record);
+ break;
+ case MX:
+ read_mx_record(buffer, record);
+ break;
+ case TXT:
+ read_txt_record(buffer, record);
+ break;
+ case AAAA:
+ read_aaaa_record(buffer, record);
+ break;
+ case SRV:
+ read_srv_record(buffer, record);
+ break;
+ case CAA:
+ read_caa_record(buffer, record, header_pos);
+ break;
+ default:
+ buffer_step(buffer, record->len);
+ return;
+ }
+
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_record(record, log);)
+ TRACE("Reading record: %s", log);
+}
+
+static void write_a_record(PacketBuffer* buffer, Record* record) {
+ ARecord data = record->data.a;
+ buffer_write_short(buffer, 4);
+ buffer_write(buffer, record->data.a.addr[0]);
+ buffer_write(buffer, data.addr[1]);
+ buffer_write(buffer, data.addr[2]);
+ buffer_write(buffer, data.addr[3]);
+}
+
+static void write_ns_record(PacketBuffer* buffer, Record* record) {
+ NSRecord data = record->data.ns;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_qname(buffer, data.host);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_cname_record(PacketBuffer* buffer, Record* record) {
+ CNAMERecord data = record->data.cname;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_qname(buffer, data.host);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_soa_record(PacketBuffer* buffer, Record* record) {
+ SOARecord data = record->data.soa;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_qname(buffer, data.mname);
+ buffer_write_qname(buffer, data.nname);
+ buffer_write_int(buffer, data.serial);
+ buffer_write_int(buffer, data.refresh);
+ buffer_write_int(buffer, data.retry);
+ buffer_write_int(buffer, data.expire);
+ buffer_write_int(buffer, data.minimum);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_ptr_record(PacketBuffer* buffer, Record* record) {
+ PTRRecord data = record->data.ptr;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_qname(buffer, data.pointer);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_mx_record(PacketBuffer* buffer, Record* record) {
+ MXRecord data = record->data.mx;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_short(buffer, data.priority);
+ buffer_write_qname(buffer, data.host);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_txt_record(PacketBuffer* buffer, Record* record) {
+ TXTRecord data = record->data.txt;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ if(data.len == 0) {
+ return;
+ }
+
+ for(uint8_t i = 0; i < data.len; i++) {
+ buffer_write_string(buffer, data.text[i]);
+ }
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_aaaa_record(PacketBuffer* buffer, Record* record) {
+ AAAARecord data = record->data.aaaa;
+
+ buffer_write_short(buffer, 16);
+
+ for (int i = 0; i < 16; i++) {
+ buffer_write(buffer, data.addr[i]);
+ }
+}
+
+static void write_srv_record(PacketBuffer* buffer, Record* record) {
+ SRVRecord data = record->data.srv;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+
+ buffer_write_short(buffer, data.priority);
+ buffer_write_short(buffer, data.weight);
+ buffer_write_short(buffer, data.port);
+ buffer_write_qname(buffer, data.target);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_caa_record(PacketBuffer* buffer, Record* record) {
+ CAARecord data = record->data.caa;
+ int pos = buffer_get_index(buffer);
+ buffer_write_short(buffer, 0);
+ buffer_write(buffer, data.flags);
+ buffer_write(buffer, data.length);
+ buffer_write_n(buffer, data.tag + 1, data.tag[0]);
+ buffer_write_n(buffer, data.value + 1, data.value[0]);
+
+ int size = buffer_get_index(buffer) - pos - 2;
+ buffer_set_uint16_t(buffer, (uint16_t)size, pos);
+}
+
+static void write_record_header(PacketBuffer* buffer, Record* record) {
+ buffer_write_qname(buffer, record->domain);
+ uint16_t id = record_to_id(record->type);
+ buffer_write_short(buffer, id);
+ buffer_write_short(buffer, record->cls);
+ buffer_write_int(buffer, record->ttl);
+}
+
+void write_record(PacketBuffer* buffer, Record* record) {
+ switch(record->type) {
+ case A:
+ write_record_header(buffer, record);
+ write_a_record(buffer, record);
+ break;
+ case NS:
+ write_record_header(buffer, record);
+ write_ns_record(buffer, record);
+ break;
+ case CNAME:
+ write_record_header(buffer, record);
+ write_cname_record(buffer, record);
+ break;
+ case SOA:
+ write_record_header(buffer, record);
+ write_soa_record(buffer, record);
+ break;
+ case PTR:
+ write_record_header(buffer, record);
+ write_ptr_record(buffer, record);
+ break;
+ case MX:
+ write_record_header(buffer, record);
+ write_mx_record(buffer, record);
+ break;
+ case TXT:
+ write_record_header(buffer, record);
+ write_txt_record(buffer, record);
+ break;
+ case AAAA:
+ write_record_header(buffer, record);
+ write_aaaa_record(buffer, record);
+ break;
+ case SRV:
+ write_record_header(buffer, record);
+ write_srv_record(buffer, record);
+ break;
+ case CAA:
+ write_record_header(buffer, record);
+ write_caa_record(buffer, record);
+ break;
+ default:
+ break;
+ }
+
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_record(record, log);)
+ TRACE("Writing record: %s", log);
+}
+
+void free_record(Record* record) {
+ free(record->domain);
+ switch (record->type) {
+ case NS:
+ free(record->data.ns.host);
+ break;
+ case CNAME:
+ free(record->data.cname.host);
+ break;
+ case SOA:
+ free(record->data.soa.mname);
+ free(record->data.soa.nname);
+ break;
+ case PTR:
+ free(record->data.ptr.pointer);
+ break;
+ case MX:
+ free(record->data.mx.host);
+ break;
+ case TXT:
+ for (uint8_t i = 0; i < record->data.txt.len; i++) {
+ free(record->data.txt.text[i]);
+ }
+ free(record->data.txt.text);
+ break;
+ case SRV:
+ free(record->data.srv.target);
+ break;
+ case CAA:
+ free(record->data.caa.value);
+ free(record->data.caa.tag);
+ break;
+ default:
+ break;
+ }
+}
+
+void print_record(Record* record, char* buffer) {
+ INIT_LOG_BOUNDS
+ switch(record->type) {
+ case UNKOWN:
+ APPEND(buffer, "UNKOWN");
+ break;
+ case A:
+ APPEND(buffer, "A (%hhu.%hhu.%hhu.%hhu)",
+ record->data.a.addr[0],
+ record->data.a.addr[1],
+ record->data.a.addr[2],
+ record->data.a.addr[3]
+ );
+ break;
+ case NS:
+ APPEND(buffer, "NS (%.*s)",
+ record->data.ns.host[0],
+ record->data.ns.host + 1
+ );
+ break;
+ case CNAME:
+ APPEND(buffer, "CNAME (%.*s)",
+ record->data.cname.host[0],
+ record->data.cname.host + 1
+ );
+ break;
+ case SOA:
+ APPEND(buffer, "SOA (%.*s %.*s %u %u %u %u %u)",
+ record->data.soa.mname[0],
+ record->data.soa.mname + 1,
+ record->data.soa.nname[0],
+ record->data.soa.nname + 1,
+ record->data.soa.serial,
+ record->data.soa.refresh,
+ record->data.soa.retry,
+ record->data.soa.expire,
+ record->data.soa.minimum
+ );
+ break;
+ case PTR:
+ APPEND(buffer, "PTR (%.*s)",
+ record->data.ptr.pointer[0],
+ record->data.ptr.pointer + 1
+ );
+ break;
+ case MX:
+ APPEND(buffer, "MX (%.*s %hu)",
+ record->data.mx.host[0],
+ record->data.mx.host + 1,
+ record->data.mx.priority
+ );
+ break;
+ case TXT:
+ APPEND(buffer, "TXT (");
+ for(uint8_t i = 0; i < record->data.txt.len; i++) {
+ APPEND(buffer, "\"%.*s\"",
+ record->data.txt.text[i][0],
+ record->data.txt.text[i] + 1
+ );
+ }
+ APPEND(buffer, ")");
+ break;
+ case AAAA:
+ APPEND(buffer, "AAAA (");
+ for(int i = 0; i < 8; i++) {
+ APPEND(buffer, "%02hhx%02hhx:",
+ record->data.a.addr[i*2 + 0],
+ record->data.a.addr[i*2 + 1]
+ );
+ }
+ APPEND(buffer, ":)");
+ break;
+ case SRV:
+ APPEND(buffer, "SRV (%hu %hu %hu %.*s)",
+ record->data.srv.priority,
+ record->data.srv.weight,
+ record->data.srv.port,
+ record->data.srv.target[0],
+ record->data.srv.target + 1
+ );
+ break;
+ case CAA:
+ APPEND(buffer, "CAA (%hhu %.*s %.*s)",
+ record->data.caa.flags,
+ record->data.caa.tag[0],
+ record->data.caa.tag + 1,
+ record->data.caa.value[0],
+ record->data.caa.value + 1
+ );
+ break;
+ }
+}
\ No newline at end of file
diff --git a/src/packet/record.h b/src/packet/record.h
new file mode 100644
index 0000000..95bbbbe
--- /dev/null
+++ b/src/packet/record.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "buffer.h"
+
+#include
+
+typedef enum {
+ UNKOWN,
+ A, // 1
+ NS, // 2
+ CNAME, // 5
+ SOA, // 6
+ PTR, // 12
+ MX, // 15
+ TXT, // 16
+ AAAA, // 28
+ SRV, // 33
+ CAA // 257
+} RecordType;
+
+uint16_t record_to_id(RecordType type);
+void record_from_id(uint16_t i, RecordType* type);
+
+typedef struct {
+ uint8_t addr[4];
+} ARecord;
+
+typedef struct {
+ uint8_t* host;
+} NSRecord;
+
+typedef struct {
+ uint8_t* host;
+} CNAMERecord;
+
+typedef struct {
+ uint8_t* mname;
+ uint8_t* nname;
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ uint32_t minimum;
+} SOARecord;
+
+typedef struct {
+ uint8_t* pointer;
+} PTRRecord;
+
+typedef struct {
+ uint16_t priority;
+ uint8_t* host;
+} MXRecord;
+
+typedef struct TXTRecord {
+ uint8_t** text;
+ uint8_t len;
+} TXTRecord;
+
+typedef struct {
+ uint8_t addr[16];
+} AAAARecord;
+
+typedef struct {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ uint8_t* target;
+} SRVRecord;
+
+typedef struct {
+ uint8_t flags;
+ uint8_t length;
+ uint8_t* tag;
+ uint8_t* value;
+} CAARecord;
+
+typedef struct {
+ uint32_t ttl;
+ uint16_t cls;
+ uint16_t len;
+ uint8_t* domain;
+ RecordType type;
+ union data {
+ ARecord a;
+ NSRecord ns;
+ CNAMERecord cname;
+ SOARecord soa;
+ PTRRecord ptr;
+ MXRecord mx;
+ TXTRecord txt;
+ AAAARecord aaaa;
+ SRVRecord srv;
+ CAARecord caa;
+ } data;
+} Record;
+
+void read_record(PacketBuffer* buffer, Record* record);
+void write_record(PacketBuffer* buffer, Record* record);
+void free_record(Record* record);
+void print_record(Record* record, char* buffer);
\ No newline at end of file
diff --git a/src/server/addr.c b/src/server/addr.c
new file mode 100644
index 0000000..982da13
--- /dev/null
+++ b/src/server/addr.c
@@ -0,0 +1,233 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "addr.h"
+#include "../io/log.h"
+
+void create_ip_addr(char* domain, IpAddr* addr) {
+ addr->type = V4;
+ memcpy(&addr->data.v4.s_addr, domain, 4);
+}
+
+void create_ip_addr6(char* domain, IpAddr* addr) {
+ addr->type = V6;
+ memcpy(&addr->data.v6.__in6_u.__u6_addr8, domain, 16);
+}
+
+void ip_addr_any(IpAddr* addr) {
+ addr->type = V4;
+ addr->data.v4.s_addr = htonl(INADDR_ANY);
+}
+
+void ip_addr_any6(IpAddr* addr) {
+ addr->type = V6;
+ addr->data.v6 = in6addr_any;
+}
+
+static struct sockaddr_in create_socket_addr_v4(IpAddr addr, uint16_t port) {
+ struct sockaddr_in socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin_family = AF_INET;
+ socketaddr.sin_port = htons(port);
+ socketaddr.sin_addr = addr.data.v4;
+ return socketaddr;
+}
+
+static struct sockaddr_in6 create_socket_addr_v6(IpAddr addr, uint16_t port) {
+ struct sockaddr_in6 socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin6_family = AF_INET6;
+ socketaddr.sin6_port = htons(port);
+ socketaddr.sin6_addr = addr.data.v6;
+ return socketaddr;
+}
+
+static size_t get_addr_len(AddrType type) {
+ if (type == V4) {
+ return sizeof(struct sockaddr_in);
+ } else if (type == V6) {
+ return sizeof(struct sockaddr_in6);
+ } else {
+ return 0;
+ }
+}
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket) {
+ socket->type = addr.type;
+ if (addr.type == V4) {
+ socket->data.v4 = create_socket_addr_v4(addr, port);
+ } else if(addr.type == V6) {
+ socket->data.v6 = create_socket_addr_v6(addr, port);
+ } else {
+ ERROR("Tried to create socketaddr with invalid protocol type");
+ exit(EXIT_FAILURE);
+ }
+ socket->len = get_addr_len(addr.type);
+}
+
+void print_socket_addr(SocketAddr* addr, char* buffer) {
+ INIT_LOG_BOUNDS
+ if(addr->type == V4) {
+ APPEND(buffer, "%hhu.%hhu.%hhu.%hhu:%hu",
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 24),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 16),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 8),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr),
+ addr->data.v4.sin_port
+ );
+ } else {
+ for(int i = 0; i < 8; i++) {
+ APPEND(buffer, "%02hhx%02hhx:",
+ addr->data.v6.sin6_addr.__in6_u.__u6_addr8[i*2 + 0],
+ addr->data.v6.sin6_addr.__in6_u.__u6_addr8[i*2 + 1]
+ );
+ }
+ APPEND(buffer, ":[%hu]", addr->data.v6.sin6_port);
+ }
+}
+
+#define ADDR_DOMAIN(addr, var) \
+ struct sockaddr* var; \
+ if (addr->type == V4) { \
+ var = (struct sockaddr*) &addr->data.v4; \
+ } else if (addr->type == V6) { \
+ var = (struct sockaddr*) &addr->data.v6; \
+ } else { \
+ return -1; \
+ }
+
+#define ADDR_AFNET(type, var) \
+ int var; \
+ if (type == V4) { \
+ var = AF_INET; \
+ } else if (type == V6) { \
+ var = AF_INET6; \
+ } else { \
+ return -1; \
+ }
+
+int32_t create_udp_socket(AddrType type, UdpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_DGRAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ clientaddr->type = socket->type;
+ clientaddr->len = get_addr_len(socket->type);
+ ADDR_DOMAIN(clientaddr, __addr)
+ return recvfrom(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_WAITALL,
+ __addr,
+ (uint32_t*) &clientaddr->len
+ );
+}
+
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ ADDR_DOMAIN(clientaddr, __addr)
+ return sendto(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_CONFIRM,
+ __addr,
+ (uint32_t) clientaddr->len
+ );
+}
+
+int32_t close_udp_socket(UdpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_STREAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max) {
+ return listen(socket->sockfd, max);
+}
+
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream) {
+ stream->clientaddr.type = socket->type;
+ memset(&stream->clientaddr, 0, sizeof(SocketAddr));
+ SocketAddr* addr = &stream->clientaddr;
+ ADDR_DOMAIN(addr, __addr)
+ stream->streamfd = accept(
+ socket->sockfd,
+ __addr,
+ (uint32_t*) &stream->clientaddr.len
+ );
+ return stream->streamfd;
+}
+
+int32_t close_tcp_socket(TcpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream) {
+ TcpSocket socket;
+ int32_t res = create_tcp_socket(servaddr->type, &socket);
+ if (res < 0) return res;
+ stream->clientaddr = *servaddr;
+ stream->streamfd = socket.sockfd;
+ ADDR_DOMAIN(servaddr, __addr)
+ return connect(
+ socket.sockfd,
+ __addr,
+ servaddr->len
+ );
+}
+
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ return recv(stream->streamfd, buffer, len, 0);
+}
+
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ return send(stream->streamfd, buffer, len, MSG_NOSIGNAL);
+}
+
+int32_t close_tcp_stream(TcpStream* stream) {
+ return close(stream->streamfd);
+}
diff --git a/src/server/addr.h b/src/server/addr.h
new file mode 100644
index 0000000..173c7fd
--- /dev/null
+++ b/src/server/addr.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "../packet/record.h"
+
+#include
+#include
+#include
+#include
+
+typedef enum {
+ V4,
+ V6
+} AddrType;
+
+typedef struct {
+ AddrType type;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } data;
+} IpAddr;
+
+void create_ip_addr(char* domain, IpAddr* addr);
+void create_ip_addr6(char* domain, IpAddr* addr);
+void ip_addr_any(IpAddr* addr);
+void ip_addr_any6(IpAddr* addr);
+
+typedef struct {
+ AddrType type;
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } data;
+ size_t len;
+} SocketAddr;
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket);
+void print_socket_addr(SocketAddr* addr, char* buffer);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} UdpSocket;
+
+int32_t create_udp_socket(AddrType type, UdpSocket* socket);
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* socket);
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t close_udp_socket(UdpSocket* socket);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} TcpSocket;
+
+typedef struct {
+ SocketAddr clientaddr;
+ uint32_t streamfd;
+} TcpStream;
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* socket);
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* socket);
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max);
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream);
+int32_t close_tcp_socket(TcpSocket* socket);
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream);
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t close_tcp_stream(TcpStream* stream);
diff --git a/src/server/binding.c b/src/server/binding.c
new file mode 100644
index 0000000..47c62c6
--- /dev/null
+++ b/src/server/binding.c
@@ -0,0 +1,245 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "addr.h"
+#include "binding.h"
+#include "../io/log.h"
+
+static void create_udp_binding(UdpSocket* socket, uint16_t port) {
+ if (create_udp_socket(V6, socket) < 0) {
+ ERROR("Failed to create UDP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ IpAddr addr;
+ ip_addr_any6(&addr);
+
+ SocketAddr socketaddr;
+ create_socket_addr(port, addr, &socketaddr);
+
+ if (bind_udp_socket(&socketaddr, socket) < 0) {
+ ERROR("Failed to bind UDP socket on port %hu: %s", port, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void create_tcp_binding(TcpSocket* socket, uint16_t port) {
+ if (create_tcp_socket(V6, socket) < 0) {
+ ERROR("Failed to create TCP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ IpAddr addr;
+ ip_addr_any6(&addr);
+
+ SocketAddr socketaddr;
+ create_socket_addr(port, addr, &socketaddr);
+
+ if (bind_tcp_socket(&socketaddr, socket) < 0) {
+ ERROR("Failed to bind TCP socket on port %hu: %s", port, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen_tcp_socket(socket, 5) < 0) {
+ ERROR("Failed to listen on TCP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void create_binding(BindingType type, uint16_t port, Binding* binding) {
+ binding->type = type;
+ if (type == UDP) {
+ create_udp_binding(&binding->sock.udp, port);
+ } else if(type == TCP) {
+ create_tcp_binding(&binding->sock.tcp, port);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+}
+
+void free_binding(Binding* binding) {
+ if (binding->type == UDP) {
+ close_udp_socket(&binding->sock.udp);
+ } else if(binding->type == TCP) {
+ close_tcp_socket(&binding->sock.tcp);
+ }
+}
+
+bool accept_connection(Binding* binding, Connection* connection) {
+ connection->type = binding->type;
+
+ if(binding->type == UDP) {
+ connection->sock.udp.udp = binding->sock.udp;
+ memset(&connection->sock.udp.clientaddr, 0, sizeof(SocketAddr));
+ return true;
+ }
+
+ if (accept_tcp_socket(&binding->sock.tcp, &connection->sock.tcp) < 0) {
+ ERROR("Failed to accept TCP connection: %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static void read_to_packet(uint8_t* buf, uint16_t len, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(len);
+ for (int i = 0; i < len; i++) {
+ buffer_write(pkbuffer, buf[i]);
+ }
+ buffer_seek(pkbuffer, 0);
+ read_packet(pkbuffer, packet);
+ buffer_free(pkbuffer);
+}
+
+static bool read_udp(Connection* connection, Packet* packet) {
+ uint8_t buffer[512];
+ int32_t n = read_udp_socket(
+ &connection->sock.udp.udp,
+ buffer,
+ 512,
+ &connection->sock.udp.clientaddr
+ );
+ if (n < 0) {
+ return false;
+ }
+ read_to_packet(buffer, n, packet);
+ return true;
+}
+
+static bool read_tcp(Connection* connection, Packet* packet) {
+ uint16_t len;
+ if ( read_tcp_stream(
+ &connection->sock.tcp,
+ &len,
+ sizeof(uint16_t)
+ ) < 0) {
+ return false;
+ }
+
+ uint8_t buffer[len];
+ if ( read_tcp_stream(
+ &connection->sock.tcp,
+ buffer,
+ len
+ ) < 0) {
+ return false;
+ }
+
+ read_to_packet(buffer, len, packet);
+ return true;
+}
+
+bool read_connection(Connection* connection, Packet* packet) {
+ if (connection->type == UDP) {
+ return read_udp(connection, packet);
+ } else if (connection->type == TCP) {
+ return read_tcp(connection, packet);
+ }
+ return false;
+}
+
+static bool write_udp(Connection* connection, uint8_t* buf, uint16_t len) {
+ //if (len > 512) {
+ buf[2] = buf[2] | 0x03;
+ // len = 512;
+ // }
+ return write_udp_socket(
+ &connection->sock.udp.udp,
+ buf,
+ len,
+ &connection->sock.udp.clientaddr
+ ) == len;
+}
+
+static bool write_tcp(Connection* connection, uint8_t* buf, uint16_t len) {
+ len = htons(len);
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ &len,
+ sizeof(uint16_t)
+ ) < 0) {
+ return false;
+ }
+
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ buf,
+ len
+ ) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool write_connection(Connection* connection, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(64);
+ write_packet(pkbuffer, packet);
+ uint16_t len = buffer_get_size(pkbuffer);
+ uint8_t* buffer = buffer_get_ptr(pkbuffer);
+ bool success = false;
+ if(connection->type == UDP) {
+ success = write_udp(connection, buffer, len);
+ } else if(connection->type == TCP) {
+ success = write_tcp(connection, buffer, len);
+ };
+ buffer_free(pkbuffer);
+ return success;
+}
+
+void free_connection(Connection* connection) {
+ if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
+
+static bool create_udp_request(SocketAddr* addr, Connection* request) {
+ if ( create_udp_socket(addr->type, &request->sock.udp.udp) < 0) {
+ ERROR("Failed to connect to UDP socket: %s", strerror(errno));
+ return false;
+ }
+ request->sock.udp.clientaddr = *addr;
+ return true;
+}
+
+static bool create_tcp_request(SocketAddr* addr, Connection* request) {
+ if( connect_tcp_stream(addr, &request->sock.tcp) < 0) {
+ ERROR("Failed to connect to TCP socket: %s", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request) {
+ request->type = type;
+ if (type == UDP) {
+ return create_udp_request(addr, request);
+ } else if (type == TCP) {
+ return create_tcp_request(addr, request);
+ } else {
+ return true;
+ }
+}
+
+bool request_packet(Connection* request, Packet* in, Packet* out) {
+ if (!write_connection(request, in)) {
+ return false;
+ }
+ if (!read_connection(request, out)) {
+ return false;
+ }
+ return true;
+}
+
+void free_request(Connection* connection) {
+ if (connection->type == UDP) {
+ close_udp_socket(&connection->sock.udp.udp);
+ } else if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
diff --git a/src/server/binding.h b/src/server/binding.h
new file mode 100644
index 0000000..e2e6160
--- /dev/null
+++ b/src/server/binding.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "../packet/packet.h"
+#include "addr.h"
+
+#include
+
+typedef enum {
+ UDP,
+ TCP
+} BindingType;
+
+typedef struct {
+ BindingType type;
+ union {
+ UdpSocket udp;
+ TcpSocket tcp;
+ } sock;
+} Binding;
+
+void create_binding(BindingType type, uint16_t port, Binding* binding);
+void free_binding(Binding* binding);
+
+typedef struct {
+ BindingType type;
+ union {
+ struct {
+ UdpSocket udp;
+ SocketAddr clientaddr;
+ } udp;
+ TcpStream tcp;
+ } sock;
+} Connection;
+
+bool accept_connection(Binding* binding, Connection* connection);
+bool read_connection(Connection* connection, Packet* packet);
+bool write_connection(Connection* connection, Packet* packet);
+void free_connection(Connection* connection);
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request);
+bool request_packet(Connection* request, Packet* in, Packet* out);
+void free_request(Connection* connection);
diff --git a/src/server/resolver.c b/src/server/resolver.c
new file mode 100644
index 0000000..e05f365
--- /dev/null
+++ b/src/server/resolver.c
@@ -0,0 +1,166 @@
+#include
+#include
+#include
+
+#include "resolver.h"
+#include "addr.h"
+#include "binding.h"
+#include "../io/log.h"
+
+static bool lookup(
+ Question* question,
+ Packet* response,
+ BindingType type,
+ SocketAddr addr
+) {
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_socket_addr(&addr, log);)
+ TRACE("Attempting lookup on fallback dns %s", log);
+
+ Connection request;
+ if (!create_request(type, &addr, &request)) {
+ return false;
+ }
+
+ Packet req;
+ memset(&req, 0, sizeof(Packet));
+ req.header.id = response->header.id;
+ req.header.opcode = response->header.opcode;
+ req.header.questions = 1;
+ req.header.recursion_desired = true;
+ req.questions = malloc(sizeof(Question));
+ req.questions[0] = *question;
+
+ if (!request_packet(&request, &req, response)) {
+ free_request(&request);
+ free(req.questions);
+ ERROR("Failed to request fallback dns: %s", strerror(errno));
+ return false;
+ }
+
+ free_request(&request);
+ free(req.questions);
+ return true;
+}
+
+static bool search(Question* question, Packet* result, BindingType type) {
+ IpAddr addr;
+ char ip[4] = {1, 1, 1, 1};
+ create_ip_addr(ip, &addr);
+
+ uint16_t port = 53;
+ SocketAddr saddr;
+ create_socket_addr(port, addr, &saddr);
+
+ while(1) {
+ if (!lookup(question, result, type, saddr)) {
+ return false;
+ }
+
+ if (result->header.answers > 0 && result->header.rescode == NOERROR) {
+ return true;
+ }
+
+ if (result->header.rescode == NXDOMAIN) {
+ return true;
+ }
+
+ if (get_resolved_ns(result, question->domain, &addr)) {
+ continue;
+ }
+
+ Question new_question;
+ if (!get_unresoled_ns(result, question->domain, &new_question)) {
+ return true;
+ }
+
+ Packet recurse;
+ if (!search(&new_question, &recurse, type)) {
+ return false;
+ }
+
+ free_question(&new_question);
+
+ IpAddr random;
+ if (!get_random_a(&recurse, &random)) {
+ free_packet(&recurse);
+ return true;
+ } else {
+ free_packet(&recurse);
+ addr = random;
+ }
+ }
+}
+
+static void push_records(Record* from, uint8_t from_len, Record** to, uint8_t to_len) {
+ if(from_len < 1) return;
+ *to = realloc(*to, sizeof(Record) * (from_len + to_len));
+ memcpy(*to + to_len, from, from_len * sizeof(Record));
+}
+
+static void push_questions(Question* from, uint8_t from_len, Question** to, uint8_t to_len) {
+ if(from_len < 1) return;
+ *to = realloc(*to, sizeof(Question) * (from_len + to_len));
+ memcpy(*to + to_len, from, from_len * sizeof(Question));
+}
+
+void handle_query(Packet* request, Packet* response, BindingType type) {
+ memset(response, 0, sizeof(Packet));
+ response->header.id = request->header.id;
+ response->header.opcode = request->header.opcode;
+ response->header.recursion_desired = true;
+ response->header.recursion_available = true;
+ response->header.response = true;
+
+ if (request->header.questions < 1) {
+ response->header.response = FORMERR;
+ return;
+ }
+
+ for (uint16_t i = 0; i < request->header.questions; i++) {
+ Packet result;
+ memset(&result, 0, sizeof(Packet));
+ result.header.id = response->header.id;
+ if (!search(&request->questions[i], &result, type)) {
+ response->header.response = SERVFAIL;
+ break;
+ }
+
+ push_questions(
+ result.questions,
+ result.header.questions,
+ &response->questions,
+ response->header.questions
+ );
+ response->header.questions += result.header.questions;
+
+ push_records(
+ result.answers,
+ result.header.answers,
+ &response->answers,
+ response->header.answers
+ );
+ response->header.answers += result.header.answers;
+
+ push_records(
+ result.authorities,
+ result.header.authoritative_entries,
+ &response->authorities,
+ response->header.authoritative_entries
+ );
+ response->header.authoritative_entries += result.header.authoritative_entries;
+
+ push_records(
+ result.resources,
+ result.header.resource_entries,
+ &response->resources,
+ response->header.resource_entries
+ );
+ response->header.resource_entries += result.header.resource_entries;
+
+ free(result.questions);
+ free(result.answers);
+ free(result.authorities);
+ free(result.resources);
+ }
+}
\ No newline at end of file
diff --git a/src/server/resolver.h b/src/server/resolver.h
new file mode 100644
index 0000000..79b4825
--- /dev/null
+++ b/src/server/resolver.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "../packet/packet.h"
+#include "binding.h"
+
+void handle_query(Packet* request, Packet* response, BindingType type);
\ No newline at end of file
diff --git a/src/server/server.c b/src/server/server.c
new file mode 100644
index 0000000..c8975ee
--- /dev/null
+++ b/src/server/server.c
@@ -0,0 +1,100 @@
+#define _POSIX_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "addr.h"
+#include "server.h"
+#include "resolver.h"
+#include "../io/log.h"
+
+static pid_t udp, tcp;
+
+void server_init(uint16_t port, Server* server) {
+ INFO("Server port set to %hu", port);
+ create_binding(UDP, port, &server->udp);
+ create_binding(TCP, port, &server->tcp);
+}
+
+static void server_listen(Binding* binding) {
+ while(1) {
+
+ Connection connection;
+ if (!accept_connection(binding, &connection)) {
+ ERROR("Failed to accept connection");
+ continue;
+ }
+
+ Packet request;
+ if (!read_connection(&connection, &request)) {
+ ERROR("Failed to read connection");
+ free_connection(&connection);
+ continue;
+ }
+
+ if(fork() != 0) {
+ free_packet(&request);
+ free_connection(&connection);
+ continue;
+ }
+
+ INFO("Recieved packet request ID %hu", request.header.id);
+
+ Packet response;
+ handle_query(&request, &response, connection.type);
+
+ if (!write_connection(&connection, &response)) {
+ ERROR("Failed to respond to connection ID %hu: %s", request.header.id, strerror(errno));
+ }
+
+ free_packet(&request);
+ free_packet(&response);
+ free_connection(&connection);
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void signal_handler() {
+ printf("\n");
+ kill(udp, SIGTERM);
+ kill(tcp, SIGTERM);
+}
+
+void server_run(Server* server) {
+ if ((udp = fork()) == 0) {
+ INFO("Listening for connections on UDP");
+ server_listen(&server->udp);
+ exit(EXIT_SUCCESS);
+ }
+
+ if ((tcp = fork()) == 0) {
+ INFO("Listening for connections on TCP");
+ server_listen(&server->tcp);
+ exit(EXIT_SUCCESS);
+ }
+
+ signal(SIGINT, signal_handler);
+
+ int status;
+ waitpid(udp, &status, 0);
+ if (status == 0) {
+ INFO("UDP process closed successfully");
+ } else {
+ ERROR("UDP process failed with error code %d", status);
+ }
+
+ waitpid(tcp, &status, 0);
+ if (status == 0) {
+ INFO("TCP process closed successfully");
+ } else {
+ ERROR("TCP process failed with error code %d", status);
+ }
+}
+
+void server_free(Server* server) {
+ free_binding(&server->udp);
+ free_binding(&server->tcp);
+}
diff --git a/src/server/server.h b/src/server/server.h
new file mode 100644
index 0000000..c9509f2
--- /dev/null
+++ b/src/server/server.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "binding.h"
+
+typedef struct {
+ Binding udp;
+ Binding tcp;
+} Server;
+
+void server_init(uint16_t port, Server* server);
+void server_run(Server* server);
+void server_free(Server* server);
\ No newline at end of file
diff --git a/src/web/api.rs b/src/web/api.rs
deleted file mode 100644
index 1fddb5f..0000000
--- a/src/web/api.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use std::net::IpAddr;
-
-use axum::{
- extract::Query,
- response::Response,
- routing::{get, post, put, delete},
- Extension, Router,
-};
-use moka::future::Cache;
-use rand::distributions::{Alphanumeric, DistString};
-use serde::Deserialize;
-use tower_cookies::{Cookie, Cookies};
-
-use crate::{config::Config, database::Database, dns::packet::record::DnsRecord};
-
-use super::{
- extract::{Authorized, Body, RequestIp},
- http::{json, text},
-};
-
-pub fn router() -> Router {
- Router::new()
- .route("/login", post(login))
- .route("/domains", get(list_domains))
- .route("/domains", delete(delete_domain))
- .route("/records", get(get_domain))
- .route("/records", put(add_record))
-}
-
-async fn list_domains(_: Authorized, Extension(database): Extension) -> Response {
- let domains = match database.get_domains().await {
- Ok(domains) => domains,
- Err(err) => return text(500, &format!("{err}")),
- };
-
- let Ok(domains) = serde_json::to_string(&domains) else {
- return text(500, "Failed to fetch domains")
- };
-
- json(200, &domains)
-}
-
-#[derive(Deserialize)]
-struct DomainRequest {
- domain: String,
-}
-
-async fn get_domain(
- _: Authorized,
- Extension(database): Extension,
- Query(query): Query,
-) -> Response {
- let records = match database.get_domain(&query.domain).await {
- Ok(records) => records,
- Err(err) => return text(500, &format!("{err}")),
- };
-
- let Ok(records) = serde_json::to_string(&records) else {
- return text(500, "Failed to fetch records")
- };
-
- json(200, &records)
-}
-
-async fn delete_domain(
- _: Authorized,
- Extension(database): Extension,
- Body(body): Body,
-) -> Response {
-
- let Ok(request) = serde_json::from_str::(&body) else {
- return text(400, "Missing request parameters")
- };
-
- let Ok(domains) = database.get_domains().await else {
- return text(500, "Failed to delete domain")
- };
-
- if !domains.contains(&request.domain) {
- return text(400, "Domain does not exist")
- }
-
- if database.delete_domain(request.domain).await.is_err() {
- return text(500, "Failed to delete domain")
- };
-
- return text(204, "Successfully deleted domain")
-}
-
-async fn add_record(
- _: Authorized,
- Extension(database): Extension,
- Body(body): Body,
-) -> Response {
- let Ok(record) = serde_json::from_str::(&body) else {
- return text(400, "Invalid DNS record")
- };
-
- let allowed = record.get_qtype().allowed_actions();
- if !allowed.1 {
- return text(400, "Not allowed to create record")
- }
-
- let Ok(records) = database.get_records(&record.get_domain(), record.get_qtype()).await else {
- return text(500, "Failed to complete record check");
- };
-
- if !records.is_empty() && !allowed.0 {
- return text(400, "Not allowed to create duplicate record")
- };
-
- if records.contains(&record) {
- return text(400, "Not allowed to create duplicate record")
- }
-
- if let Err(err) = database.add_record(record).await {
- return text(500, &format!("{err}"));
- }
-
- return text(201, "Added record to database successfully");
-}
-
-#[derive(Deserialize)]
-struct LoginRequest {
- user: String,
- pass: String,
-}
-
-async fn login(
- Extension(config): Extension,
- Extension(cache): Extension>,
- RequestIp(ip): RequestIp,
- cookies: Cookies,
- Body(body): Body,
-) -> Response {
- let Ok(request) = serde_json::from_str::(&body) else {
- return text(400, "Missing request parameters")
- };
-
- if request.user != config.web_user || request.pass != config.web_pass {
- return text(400, "Invalid credentials");
- };
-
- let token = Alphanumeric.sample_string(&mut rand::thread_rng(), 128);
-
- cache.insert(token.clone(), ip).await;
-
- let mut cookie = Cookie::new("auth", token);
- cookie.set_secure(true);
- cookie.set_http_only(true);
- cookie.set_path("/");
-
- cookies.add(cookie);
-
- text(200, "Successfully logged in")
-}
diff --git a/src/web/extract.rs b/src/web/extract.rs
deleted file mode 100644
index 4b6cd7c..0000000
--- a/src/web/extract.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-use std::{
- io::Read,
- net::{IpAddr, SocketAddr},
-};
-
-use axum::{
- async_trait,
- body::HttpBody,
- extract::{ConnectInfo, FromRequest, FromRequestParts},
- http::{request::Parts, Request},
- response::Response,
- BoxError,
-};
-use bytes::Bytes;
-use moka::future::Cache;
-use tower_cookies::Cookies;
-
-use super::http::text;
-
-pub struct Authorized;
-
-#[async_trait]
-impl
FromRequestParts for Authorized
-where
- S: Send + Sync,
-{
- type Rejection = Response;
-
- async fn from_request_parts(parts: &mut Parts, state: &S) -> Result {
- let Ok(Some(cookies)) = Option::::from_request_parts(parts, state).await else {
- return Err(text(403, "No cookies provided"))
- };
-
- let Some(token) = cookies.get("auth") else {
- return Err(text(403, "No auth token provided"))
- };
-
- let auth_ip: IpAddr;
- {
- let Some(cache) = parts.extensions.get::>() else {
- return Err(text(500, "Failed to load auth store"))
- };
-
- let Some(ip) = cache.get(token.value()) else {
- return Err(text(401, "Unauthorized"))
- };
-
- auth_ip = ip
- }
-
- let Ok(Some(RequestIp(ip))) = Option::::from_request_parts(parts, state).await else {
- return Err(text(403, "You have no ip"))
- };
-
- if auth_ip != ip {
- return Err(text(403, "Auth token does not match current ip"));
- }
-
- Ok(Self)
- }
-}
-
-pub struct RequestIp(pub IpAddr);
-
-#[async_trait]
-impl FromRequestParts for RequestIp
-where
- S: Send + Sync,
-{
- type Rejection = Response;
-
- async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result {
- let headers = &parts.headers;
-
- let forwardedfor = headers
- .get("x-forwarded-for")
- .and_then(|h| h.to_str().ok())
- .and_then(|h| {
- h.split(',')
- .rev()
- .find_map(|s| s.trim().parse::().ok())
- });
-
- if let Some(forwardedfor) = forwardedfor {
- return Ok(Self(forwardedfor));
- }
-
- let realip = headers
- .get("x-real-ip")
- .and_then(|hv| hv.to_str().ok())
- .and_then(|s| s.parse::().ok());
-
- if let Some(realip) = realip {
- return Ok(Self(realip));
- }
-
- let realip = headers
- .get("x-real-ip")
- .and_then(|hv| hv.to_str().ok())
- .and_then(|s| s.parse::().ok());
-
- if let Some(realip) = realip {
- return Ok(Self(realip));
- }
-
- let info = parts.extensions.get::>();
-
- if let Some(info) = info {
- return Ok(Self(info.0.ip()));
- }
-
- Err(text(403, "You have no ip"))
- }
-}
-
-pub struct Body(pub String);
-
-#[async_trait]
-impl FromRequest for Body
-where
- B: HttpBody + Sync + Send + 'static,
- B::Data: Send,
- B::Error: Into,
- S: Send + Sync,
-{
- type Rejection = Response;
-
- async fn from_request(req: Request, state: &S) -> Result {
- let Ok(bytes) = Bytes::from_request(req, state).await else {
- return Err(text(413, "Payload too large"));
- };
-
- let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
- return Err(text(400, "Invalid utf8 body"))
- };
-
- Ok(Self(body))
- }
-}
diff --git a/src/web/file.rs b/src/web/file.rs
deleted file mode 100644
index 73ecdc9..0000000
--- a/src/web/file.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use axum::{extract::Path, response::Response};
-
-use super::http::serve;
-
-pub async fn js(Path(path): Path) -> Response {
- let path = format!("/js/{path}");
- serve(&path).await
-}
-
-pub async fn css(Path(path): Path) -> Response {
- let path = format!("/css/{path}");
- serve(&path).await
-}
-
-pub async fn fonts(Path(path): Path) -> Response {
- let path = format!("/fonts/{path}");
- serve(&path).await
-}
-
-pub async fn image(Path(path): Path) -> Response {
- let path = format!("/image/{path}");
- serve(&path).await
-}
-
-pub async fn favicon() -> Response {
- serve("/favicon.ico").await
-}
-
-pub async fn robots() -> Response {
- serve("/robots.txt").await
-}
diff --git a/src/web/http.rs b/src/web/http.rs
deleted file mode 100644
index 7ab1b11..0000000
--- a/src/web/http.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use axum::{
- body::Body,
- http::{header::HeaderName, HeaderValue, Request, StatusCode},
- response::{IntoResponse, Response},
-};
-use std::str;
-use tower::ServiceExt;
-use tower_http::services::ServeFile;
-
-pub fn text(code: u16, msg: &str) -> Response {
- (status_code(code), msg.to_owned()).into_response()
-}
-
-pub fn json(code: u16, json: &str) -> Response {
- let mut res = (status_code(code), json.to_owned()).into_response();
- res.headers_mut().insert(
- HeaderName::from_static("content-type"),
- HeaderValue::from_static("application/json"),
- );
- res
-}
-
-pub async fn serve(path: &str) -> Response {
- if !path.chars().any(|c| c == '.') {
- return text(403, "Invalid file path");
- }
-
- let path = format!("public{path}");
- let file = ServeFile::new(path);
-
- let Ok(mut res) = file.oneshot(Request::new(Body::empty())).await else {
- tracing::error!("Error while fetching file");
- return text(500, "Error when fetching file")
- };
-
- if res.status() != StatusCode::OK {
- return text(404, "File not found");
- }
-
- res.headers_mut().insert(
- HeaderName::from_static("cache-control"),
- HeaderValue::from_static("max-age=300"),
- );
-
- res.into_response()
-}
-
-fn status_code(code: u16) -> StatusCode {
- StatusCode::from_u16(code).map_or(StatusCode::OK, |code| code)
-}
diff --git a/src/web/mod.rs b/src/web/mod.rs
deleted file mode 100644
index 530a3f9..0000000
--- a/src/web/mod.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-use std::net::{IpAddr, SocketAddr, TcpListener};
-use std::time::Duration;
-
-use axum::routing::get;
-use axum::{Extension, Router};
-use moka::future::Cache;
-use tokio::task::JoinHandle;
-use tower_cookies::CookieManagerLayer;
-use tracing::{error, info};
-
-use crate::config::Config;
-use crate::database::Database;
-use crate::Result;
-
-mod api;
-mod extract;
-mod file;
-mod http;
-mod pages;
-
-pub struct WebServer {
- config: Config,
- database: Database,
- addr: SocketAddr,
-}
-
-impl WebServer {
- pub async fn new(config: Config, database: Database) -> Result {
- let addr = format!("[::]:{}", config.web_port).parse::()?;
- Ok(Self {
- config,
- database,
- addr,
- })
- }
-
- pub async fn run(&self) -> Result> {
- let config = self.config.clone();
- let database = self.database.clone();
- let listener = TcpListener::bind(self.addr)?;
-
- info!(
- "Listening for HTTP traffic on [::]:{}",
- self.config.web_port
- );
-
- let app = Self::router(config, database);
- let server = axum::Server::from_tcp(listener)?;
-
- let web_handle = tokio::spawn(async move {
- if let Err(err) = server
- .serve(app.into_make_service_with_connect_info::())
- .await
- {
- error!("{err}");
- }
- });
-
- Ok(web_handle)
- }
-
- fn router(config: Config, database: Database) -> Router {
- let cache: Cache = Cache::builder()
- .time_to_live(Duration::from_secs(60 * 15))
- .max_capacity(config.dns_cache_size)
- .build();
-
- Router::new()
- .nest("/", pages::router())
- .nest("/api", api::router())
- .layer(Extension(config))
- .layer(Extension(cache))
- .layer(Extension(database))
- .layer(CookieManagerLayer::new())
- .route("/js/*path", get(file::js))
- .route("/css/*path", get(file::css))
- .route("/fonts/*path", get(file::fonts))
- .route("/image/*path", get(file::image))
- .route("/favicon.ico", get(file::favicon))
- .route("/robots.txt", get(file::robots))
- }
-}
diff --git a/src/web/pages.rs b/src/web/pages.rs
deleted file mode 100644
index a8605ef..0000000
--- a/src/web/pages.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use axum::{response::Response, routing::get, Router};
-
-use super::{extract::Authorized, http::serve};
-
-pub fn router() -> Router {
- Router::new()
- .route("/", get(root))
- .route("/login", get(login))
- .route("/home", get(home))
- .route("/domain", get(domain))
-}
-
-async fn root(user: Option) -> Response {
- if user.is_some() {
- home().await
- } else {
- login().await
- }
-}
-
-async fn login() -> Response {
- serve("/login.html").await
-}
-
-async fn home() -> Response {
- serve("/home.html").await
-}
-
-async fn domain() -> Response {
- serve("/domain.html").await
-}