Compare commits

...

4 commits

Author SHA1 Message Date
52bee5577f Lists of black. 2025-05-27 09:02:20 +09:00
23ce1f80a5
Papers please. 2025-05-26 17:39:02 +09:00
70bdcef3e7 actually commit 2025-05-26 13:07:12 +09:00
b6e4213dd0 Glory to Arstotzka. 2025-05-26 12:44:06 +09:00
9 changed files with 869 additions and 110 deletions

620
Cargo.lock generated
View file

@ -43,6 +43,12 @@ dependencies = [
"syn",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.4.0"
@ -60,9 +66,9 @@ dependencies = [
"bitflags 1.3.2",
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"itoa",
"matchit",
"memchr",
@ -74,9 +80,9 @@ dependencies = [
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"sync_wrapper 0.1.2",
"tokio",
"tower",
"tower 0.4.13",
"tower-layer",
"tower-service",
]
@ -90,8 +96,8 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http 0.2.12",
"http-body 0.4.6",
"mime",
"rustversion",
"tower-layer",
@ -110,9 +116,15 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -125,6 +137,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
]
[[package]]
name = "bumpalo"
version = "3.17.0"
@ -170,6 +191,51 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cityhash-rs"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a719913643003b84bd13022b4b7e703c09342cd03b679c4641c7d2e50dc34d"
[[package]]
name = "clickhouse"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9894248c4c5a4402f76a56c273836a0c32547ec8a68166aedee7e01b7b8d102"
dependencies = [
"bstr",
"bytes",
"cityhash-rs",
"clickhouse-derive",
"futures",
"futures-channel",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"lz4_flex",
"quanta",
"replace_with",
"sealed",
"serde",
"static_assertions",
"thiserror 1.0.69",
"time",
"tokio",
"url",
]
[[package]]
name = "clickhouse-derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d70f3e2893f7d3e017eeacdc9a708fbc29a10488e3ebca21f9df6a5d2b616dbb"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "combine"
version = "4.6.7"
@ -216,6 +282,15 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "deranged"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
@ -227,6 +302,15 @@ dependencies = [
"syn",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.10.2"
@ -277,6 +361,21 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
@ -284,6 +383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -292,6 +392,34 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
@ -316,9 +444,13 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@ -348,7 +480,7 @@ dependencies = [
"libc",
"wasi 0.13.3+wasi-0.2.2",
"wasm-bindgen",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -380,6 +512,25 @@ dependencies = [
"web-time",
]
[[package]]
name = "h2"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.3.1",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@ -414,6 +565,17 @@ dependencies = [
"itoa",
]
[[package]]
name = "http"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.6"
@ -421,7 +583,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http",
"http 0.2.12",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http 1.3.1",
]
[[package]]
name = "http-body-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http 1.3.1",
"http-body 1.0.1",
"pin-project-lite",
]
@ -453,8 +638,8 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
@ -466,6 +651,63 @@ dependencies = [
"want",
]
[[package]]
name = "hyper"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
"http 1.3.1",
"http-body 1.0.1",
"httparse",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.27.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d"
dependencies = [
"http 1.3.1",
"hyper 1.6.0",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.0",
]
[[package]]
name = "hyper-util"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.3.1",
"http-body 1.0.1",
"hyper 1.6.0",
"libc",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
]
[[package]]
name = "icu_collections"
version = "1.5.0"
@ -611,6 +853,22 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown 0.15.3",
]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "is-terminal"
version = "0.4.15"
@ -660,9 +918,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.170"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "litemap"
@ -686,6 +944,12 @@ version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
name = "lz4_flex"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
[[package]]
name = "matchit"
version = "0.7.3"
@ -740,6 +1004,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
@ -799,7 +1069,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -846,6 +1116,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@ -884,6 +1160,7 @@ name = "quiclime"
version = "0.1.0"
dependencies = [
"axum",
"clickhouse",
"env_logger",
"eyre",
"governor",
@ -892,10 +1169,12 @@ dependencies = [
"parking_lot",
"quinn",
"rand 0.9.0",
"reqwest",
"rustls-pemfile",
"serde",
"serde_json",
"thiserror 1.0.69",
"time",
"tokio",
]
@ -1069,6 +1348,57 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "replace_with"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884"
[[package]]
name = "reqwest"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-rustls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"once_cell",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.2",
"tokio",
"tokio-rustls",
"tower 0.5.2",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots 0.26.11",
"windows-registry",
]
[[package]]
name = "ring"
version = "0.17.11"
@ -1214,6 +1544,17 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sealed"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "security-framework"
version = "2.11.1"
@ -1258,6 +1599,17 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.139"
@ -1315,9 +1667,9 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@ -1338,6 +1690,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "subtle"
version = "2.6.1"
@ -1361,6 +1719,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
dependencies = [
"futures-core",
]
[[package]]
name = "synstructure"
version = "0.13.1"
@ -1421,6 +1788,25 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "tinystr"
version = "0.7.6"
@ -1473,6 +1859,29 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -1489,6 +1898,21 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper 1.0.2",
"tokio",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
@ -1539,6 +1963,17 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "utf16_iter"
version = "1.0.5"
@ -1593,6 +2028,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
@ -1610,6 +2046,19 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
dependencies = [
"cfg-if",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
@ -1671,6 +2120,24 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.0",
]
[[package]]
name = "webpki-roots"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -1702,13 +2169,48 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings",
"windows-targets 0.53.0",
]
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -1717,7 +2219,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -1726,14 +2228,30 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
@ -1742,48 +2260,96 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"

View file

@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0"
[dependencies]
axum = "0.6.18"
clickhouse = { version = "0.13.2", features = ["inserter", "time"] }
env_logger = "0.10.0"
eyre = "0.6.12"
governor = "0.10.0"
@ -16,10 +17,12 @@ log = "0.4.19"
parking_lot = "0.12.3"
quinn = "0.11.6"
rand = "0.9.0"
reqwest = { version = "0.12.15", default-features = false, features = ["charset", "http2", "json", "rustls-tls"] }
rustls-pemfile = "2"
serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.97"
thiserror = "1.0.40"
time = "0.3.41"
tokio = { version = "1.28.2", features = ["rt-multi-thread", "fs", "macros", "io-util", "net"] }
[profile.release]

View file

@ -115,6 +115,42 @@
example = "/path/to/key.pem";
description = lib.mdDoc "Path to TLS key to use for quiclime connections.";
};
clickhouseUrl = mkOption {
type = types.str;
example = "http://clickhouse:8123";
description = lib.mdDoc "Clickhouse URL to submit metrics to.";
};
clickhouseUser = mkOption {
type = types.str;
example = "quiclime";
description = lib.mdDoc "Clickhouse user.";
};
clickhousePasswordPath = mkOption {
type = types.str;
example = "/clickhouse_password";
description = lib.mdDoc "Path to file containing the Clickhouse user's password.";
};
clickhouseDatabase = mkOption {
type = types.str;
example = "default";
description = lib.mdDoc "Name of Clickhouse database.";
};
clickhouseTable = mkOption {
type = types.str;
example = "mc_connections";
description = lib.mdDoc "Name of Clickhouse table.";
};
blocklistUrl = mkOption {
type = types.str;
example = "http://blocklists/blocklist.json";
description = lib.mdDoc "URL of IP blocklist.";
};
};
};
@ -141,6 +177,12 @@
QUICLIME_BIND_ADDR_WEB = cfg.controlAddr;
QUICLIME_CERT_PATH = cfg.cert;
QUICLIME_KEY_PATH = cfg.key;
CLICKHOUSE_URL = cfg.clickhouseUrl;
CLICKHOUSE_USER = cfg.clickhouseUser;
CLICKHOUSE_PASSWORD_PATH = cfg.clickhousePasswordPath;
CLICKHOUSE_DB = cfg.clickhouseDatabase;
CLICKHOUSE_TABLE = cfg.clickhouseTable;
BLOCKLIST_URL = cfg.blocklistUrl;
};
};

View file

@ -0,0 +1,3 @@
{
"text": "You have been blocked due to suspicious activity. If you believe this was made in error, please contact the developer."
}

View file

@ -2,24 +2,35 @@
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
use std::{convert::Infallible, net::SocketAddr, sync::Arc, time::Duration};
use std::{
collections::HashMap,
convert::Infallible,
net::{Ipv6Addr, SocketAddr},
sync::Arc,
time::Duration,
};
use axum::{
http::StatusCode,
routing::{get, post},
};
use clickhouse::{inserter::Inserter, Row};
use eyre::{eyre, Context};
use log::{error, info, warn};
use netty::{Handshake, ReadError};
use parking_lot::RwLock;
use quinn::{
crypto::rustls::QuicServerConfig,
rustls::pki_types::{CertificateDer, PrivateKeyDer},
ConnectionError, Endpoint, Incoming, ServerConfig, TransportConfig,
};
use routing::{RoutingError, RoutingTable};
use routing::RoutingTable;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
sync::Mutex,
};
use crate::{
@ -64,6 +75,17 @@ async fn create_server_config() -> eyre::Result<ServerConfig> {
Ok(config)
}
#[derive(Row, Serialize)]
struct Connection {
#[serde(with = "clickhouse::serde::time::datetime")]
established: OffsetDateTime,
region: &'static str,
client: Ipv6Addr,
intent: &'static str,
outcome: &'static str,
target: String,
}
#[tokio::main]
async fn main() -> eyre::Result<()> {
env_logger::init();
@ -78,11 +100,34 @@ async fn main() -> eyre::Result<()> {
let routing_table = Box::leak(Box::new(routing::RoutingTable::new(
std::env::var("QUICLIME_BASE_DOMAIN").context("Reading QUICLIME_BASE_DOMAIN")?,
)));
let client = clickhouse::Client::default()
.with_url(std::env::var("CLICKHOUSE_URL").context("Reading CLICKHOUSE_URL")?)
.with_user(std::env::var("CLICKHOUSE_USER").context("Reading CLICKHOUSE_USER")?)
.with_password(
tokio::fs::read_to_string(
std::env::var("CLICKHOUSE_PASSWORD_PATH")
.context("Reading CLICKHOUSE_PASSWORD_PATH")?,
)
.await
.context("Reading from CLICKHOUSE_PASSWORD_PATH")?,
)
.with_database(std::env::var("CLICKHOUSE_DB").context("Reading CLICKHOUSE_DB")?);
let inserter: clickhouse::inserter::Inserter<Connection> = client
.inserter(&std::env::var("CLICKHOUSE_TABLE").context("Reading CLICKHOUSE_TABLE")?)?
.with_timeouts(Some(Duration::from_secs(5)), Some(Duration::from_secs(20)))
.with_max_bytes(50_000_000)
.with_max_rows(750_000)
.with_period(Some(Duration::from_secs(15)));
let inserter = Arc::new(Mutex::new(inserter));
let blocklist = Arc::new(RwLock::new(HashMap::new()));
#[allow(unreachable_code)]
tokio::try_join!(
listen_quic(endpoint, routing_table),
listen_control(endpoint, routing_table),
listen_minecraft(routing_table)
listen_minecraft(routing_table, inserter.clone(), blocklist.clone()),
send_commits(inserter),
refresh_bl(blocklist)
)?;
Ok(())
}
@ -161,7 +206,7 @@ async fn try_handle_quic(connection: Incoming, routing_table: &RoutingTable) ->
async fn handle_quic(connection: Incoming, routing_table: &RoutingTable) {
if let Err(e) = try_handle_quic(connection, routing_table).await {
error!("Error handling QUIClime connection: {:#}", e);
};
}
info!("Finished handling QUIClime connection");
}
@ -219,7 +264,10 @@ async fn listen_control(
async fn try_handle_minecraft(
mut connection: TcpStream,
routing_table: &'static RoutingTable,
inserter: Arc<Mutex<Inserter<Connection>>>,
blocklist: Arc<RwLock<HashMap<Ipv6Addr, BlocklistStatus>>>,
) -> eyre::Result<()> {
let established = OffsetDateTime::now_utc();
let peer = connection.peer_addr()?;
info!("Minecraft client connected from: {}", peer);
let handshake = netty::read_packet(&mut connection, 512).await;
@ -233,17 +281,76 @@ async fn try_handle_minecraft(
let Some(address) = handshake.normalized_address() else {
return politely_disconnect(connection, handshake).await;
};
let (mut send_host, mut recv_host) =
match routing_table.route_limited(&address, peer.ip()).await {
Ok(val) => val,
Err(RoutingError::InvalidDomain) => {
return politely_disconnect(connection, handshake).await;
let target = address.clone();
let trace = |outcome| {
tokio::task::spawn(async move {
if let Err(e) = inserter.lock().await.write(&Connection {
established,
region: routing_table.base_domain(),
client: match peer.ip() {
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(),
std::net::IpAddr::V6(ipv6_addr) => ipv6_addr,
},
intent: match handshake.next_state {
netty::HandshakeType::Status => "status",
netty::HandshakeType::Login => "login",
},
outcome,
target,
}) {
error!("Failed to send telemetry: {e:?}");
}
Err(RoutingError::RateLimited) => {
warn!("Connection from {} has been rate limited!", peer);
return impolitely_disconnect(connection, handshake).await;
}
};
});
};
if routing_table.ratelimit(peer.ip()) {
trace("ratelimited");
return disconnect(
connection,
handshake,
include_str!("./serverlistping_response_rate.json"),
include_str!("./disconnect_response_rate.json"),
)
.await;
}
let bl_status = blocklist
.read()
.get(&match peer.ip() {
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(),
std::net::IpAddr::V6(ipv6_addr) => ipv6_addr,
})
.copied();
if bl_status == Some(BlocklistStatus::ShadowBanned) {
trace("shadowbanned");
return politely_disconnect(connection, handshake).await;
} else if bl_status == Some(BlocklistStatus::Blocked) {
trace("blocked");
return disconnect(
connection,
handshake,
include_str!("./serverlistping_response_blocked.json"),
include_str!("./disconnect_response_blocked.json"),
)
.await;
} else if bl_status == Some(BlocklistStatus::PingMasked)
&& handshake.next_state == netty::HandshakeType::Status
{
trace("ping_masked");
return disconnect(
connection,
handshake,
include_str!("./serverlistping_response_masked.json"),
include_str!("./disconnect_response_blocked.json"),
)
.await;
}
let routing_result = routing_table.route(&address).await;
let routing_ok = routing_result.is_some();
trace(if routing_ok { "ok" } else { "bad_domain" });
let Some((mut send_host, mut recv_host)) = routing_result else {
return politely_disconnect(connection, handshake).await;
};
handshake.send(&mut send_host).await?;
let (mut recv_client, mut send_client) = connection.split();
tokio::select! {
@ -258,53 +365,21 @@ async fn try_handle_minecraft(
Ok(())
}
async fn politely_disconnect(mut connection: TcpStream, handshake: Handshake) -> eyre::Result<()> {
match handshake.next_state {
netty::HandshakeType::Status => {
let packet = netty::read_packet(&mut connection, 1).await?;
let mut packet = packet.as_slice();
let id = packet.read_varint()?;
if id != 0 {
return Err(eyre!(
"Packet isn't a Status Request(0x00), but {:#04x}",
id
));
}
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./serverlistping_response.json"))
.await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
let packet = netty::read_packet(&mut connection, 9).await?;
let mut packet = packet.as_slice();
let id = packet.read_varint()?;
if id != 1 {
return Err(eyre!("Packet isn't a Ping Request(0x01), but {:#04x}", id));
}
let payload = packet.read_long()?;
let mut buf = Vec::with_capacity(1 + 8);
buf.write_varint(1).await?;
buf.write_u64(payload).await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
}
netty::HandshakeType::Login => {
let _ = netty::read_packet(&mut connection, 128).await?;
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./disconnect_response.json"))
.await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
}
}
Ok(())
async fn politely_disconnect(connection: TcpStream, handshake: Handshake) -> eyre::Result<()> {
disconnect(
connection,
handshake,
include_str!("./serverlistping_response.json"),
include_str!("./disconnect_response.json"),
)
.await
}
async fn impolitely_disconnect(
async fn disconnect(
mut connection: TcpStream,
handshake: Handshake,
slp_resp: &str,
dc_resp: &str,
) -> eyre::Result<()> {
match handshake.next_state {
netty::HandshakeType::Status => {
@ -319,8 +394,7 @@ async fn impolitely_disconnect(
}
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./serverlistping_response_rate.json"))
.await?;
buf.write_string(slp_resp).await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
let packet = netty::read_packet(&mut connection, 9).await?;
@ -340,8 +414,7 @@ async fn impolitely_disconnect(
let _ = netty::read_packet(&mut connection, 128).await?;
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./disconnect_response_rate.json"))
.await?;
buf.write_string(dc_resp).await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
}
@ -349,13 +422,22 @@ async fn impolitely_disconnect(
Ok(())
}
async fn handle_minecraft(connection: TcpStream, routing_table: &'static RoutingTable) {
if let Err(e) = try_handle_minecraft(connection, routing_table).await {
async fn handle_minecraft(
connection: TcpStream,
routing_table: &'static RoutingTable,
inserter: Arc<Mutex<Inserter<Connection>>>,
blocklist: Arc<RwLock<HashMap<Ipv6Addr, BlocklistStatus>>>,
) {
if let Err(e) = try_handle_minecraft(connection, routing_table, inserter, blocklist).await {
error!("Error handling Minecraft connection: {:#}", e);
};
}
}
async fn listen_minecraft(routing_table: &'static RoutingTable) -> eyre::Result<Infallible> {
async fn listen_minecraft(
routing_table: &'static RoutingTable,
inserter: Arc<Mutex<Inserter<Connection>>>,
blocklist: Arc<RwLock<HashMap<Ipv6Addr, BlocklistStatus>>>,
) -> eyre::Result<Infallible> {
let server = tokio::net::TcpListener::bind(
std::env::var("QUICLIME_BIND_ADDR_MC")
.context("Reading QUICLIME_BIND_ADDR_MC")?
@ -365,7 +447,12 @@ async fn listen_minecraft(routing_table: &'static RoutingTable) -> eyre::Result<
loop {
match server.accept().await {
Ok((connection, _)) => {
tokio::spawn(handle_minecraft(connection, routing_table));
tokio::spawn(handle_minecraft(
connection,
routing_table,
inserter.clone(),
blocklist.clone(),
));
}
Err(e) => {
error!("Error accepting minecraft connection: {:#}", e);
@ -373,3 +460,38 @@ async fn listen_minecraft(routing_table: &'static RoutingTable) -> eyre::Result<
}
}
}
async fn send_commits(inserter: Arc<Mutex<Inserter<Connection>>>) -> eyre::Result<Infallible> {
loop {
if let Err(e) = inserter.lock().await.commit().await {
error!("Error committing: {e:?}");
}
tokio::time::sleep(Duration::from_secs(5)).await;
}
}
#[derive(Deserialize, Clone, Copy, PartialEq, Eq)]
enum BlocklistStatus {
ShadowBanned,
Blocked,
PingMasked,
}
async fn refresh_bl(
blocklist: Arc<RwLock<HashMap<Ipv6Addr, BlocklistStatus>>>,
) -> eyre::Result<Infallible> {
let client = reqwest::Client::new();
let url = std::env::var("BLOCKLIST_URL").context("Reading BLOCKLIST_URL")?;
loop {
tokio::time::sleep(Duration::from_secs(10)).await;
let Ok(resp) = client.get(&url).send().await else {
error!("Failed to fetch blocklist!");
continue;
};
let Ok(json) = resp.json::<HashMap<Ipv6Addr, BlocklistStatus>>().await else {
error!("Failed to fetch blocklist!");
continue;
};
*blocklist.write() = json;
}
}

View file

@ -165,7 +165,7 @@ pub struct Handshake {
pub next_state: HandshakeType,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum HandshakeType {
Status = 1,

View file

@ -27,11 +27,6 @@ pub struct RoutingTable {
limiter: DefaultKeyedRateLimiter<IpAddr>,
}
pub enum RoutingError {
InvalidDomain,
RateLimited,
}
impl RoutingTable {
pub fn new(base_domain: String) -> Self {
RoutingTable {
@ -53,24 +48,22 @@ impl RoutingTable {
}
}
pub async fn route_limited(
&self,
domain: &str,
ip: IpAddr,
) -> Result<(SendStream, RecvStream), RoutingError> {
pub fn ratelimit(&self, ip: IpAddr) -> bool {
if self.limiter.check_key(&ip).is_err() {
return Err(RoutingError::RateLimited);
return true;
}
self.limiter.retain_recent();
false
}
pub async fn route(&self, domain: &str) -> Option<(SendStream, RecvStream)> {
let (send, recv) = oneshot::channel();
self.table
.read()
.get(domain)
.ok_or(RoutingError::InvalidDomain)?
.get(domain)?
.send(RouterRequest::RouteRequest(send))
.ok()
.ok_or(RoutingError::InvalidDomain)?;
recv.await.ok().ok_or(RoutingError::InvalidDomain)
.ok()?;
recv.await.ok()
}
fn random_domain(&self) -> String {
@ -102,6 +95,10 @@ impl RoutingTable {
parent: self,
}
}
pub fn base_domain(&self) -> &str {
&self.base_domain
}
}
#[allow(clippy::module_name_repetitions)]

View file

@ -0,0 +1,13 @@
{
"version": {
"name": "e4mc",
"protocol": -1
},
"players": {
"max": 0,
"online": 0
},
"description": {
"text": "You have been blocked due to suspicious activity."
}
}

View file

@ -0,0 +1,13 @@
{
"version": {
"name": "e4mc",
"protocol": -1
},
"players": {
"max": 0,
"online": 0
},
"description": {
"text": "Information hidden due to suspicious activity."
}
}