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 -}