Your locally hosted lumina server for IDAPro

Compare changes

Choose any two refs to compare.

+5
.github/ISSUE_TEMPLATE/config.yml
··· 1 + blank_issues_enabled: false 2 + contact_links: 3 + - name: Community support 4 + url: https://github.com/naim94a/lumen/discussions 5 + about: Please ask questions here
+40
.github/workflows/docker.yml
··· 1 + name: Deploy Docker image 2 + on: 3 + push: 4 + branches: 5 + - master 6 + 7 + env: 8 + REGISTRY: ghcr.io 9 + IMAGE_NAME: ${{ github.repository }} 10 + 11 + jobs: 12 + deploy: 13 + runs-on: ubuntu-latest 14 + permissions: 15 + contents: read 16 + packages: write 17 + 18 + steps: 19 + - name: Checkout repo 20 + uses: actions/checkout@v3 21 + 22 + - name: Login to GitHub container registry 23 + uses: docker/login-action@v2 24 + with: 25 + registry: ${{ env.REGISTRY }} 26 + username: ${{ github.repository_owner }} 27 + password: ${{ secrets.GITHUB_TOKEN }} 28 + 29 + - name: Extract metadata for Docker 30 + id: meta 31 + uses: docker/metadata-action@v4 32 + with: 33 + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 34 + 35 + - name: Build and push Docker image 36 + uses: docker/build-push-action@v4 37 + with: 38 + context: . 39 + push: true 40 + tags: ${{ steps.meta.outputs.tags }}
+2
.github/workflows/rust.yml
··· 35 35 toolchain: stable 36 36 override: true 37 37 profile: minimal 38 + - name: Code Formatting 39 + run: cargo fmt --check 38 40 - name: Build 39 41 run: cargo build 40 42 - name: Run tests
+55
CHANGELOG.md
··· 1 + # Changelog 2 + 3 + ## [Unreleased] - _TBD_ 4 + 5 + ## [v0.4.0] - 2024-03-19 6 + 7 + ### Added 8 + 9 + - Implemented the function histories command. 10 + - Configurable time limits. 11 + 12 + ### Fixed 13 + 14 + - RPC HelloResult will now report if deletes are enabled. 15 + 16 + ### Changes 17 + 18 + - Applied code formatting using `cargo fmt` 19 + 20 + ## [v0.3.0] - 2023-08-22 21 + 22 + ### Added 23 + 24 + - This changelog. 25 + - Support for IDA 8.1+ delete command. 26 + - Pooling for connections to database. 27 + - Attempt to cancel immutable database queries if client leaves. 28 + - Database migrations via Diesel ORM. 29 + - Support for IDA 8.3+ hello response. 30 + - Add Metrics for prometheus. 31 + 32 + ### Fixed 33 + 34 + - 8K stack size is too small for debug builds. 35 + 36 + ## [v0.2.0] - 2022-10-12 37 + 38 + ### Added 39 + 40 + - Protocol: support for IDA 8.1+ user authentication. 41 + - Client connection duration limitations. 42 + 43 + ### Changed 44 + 45 + - Tokio's thread size is reduced from 4M to 8K. 46 + 47 + ## [v0.1.0] - 2021-01-21 48 + 49 + This is Lumen's first tagged release. It contains a few fixes and dependency updates since the initial commit (2020-12-17). 50 + 51 + [Unreleased]: https://github.com/naim94a/lumen/compare/v0.4.0...HEAD 52 + [v0.4.0]: https://github.com/naim94a/lumen/compare/v0.3.0...v0.4.0 53 + [v0.3.0]: https://github.com/naim94a/lumen/compare/v0.2.0...v0.3.0 54 + [v0.2.0]: https://github.com/naim94a/lumen/compare/v0.1.0...v0.2.0 55 + [v0.1.0]: https://github.com/naim94a/lumen/releases/tag/v0.1.0
+1260 -457
Cargo.lock
··· 1 1 # This file is automatically @generated by Cargo. 2 2 # It is not intended for manual editing. 3 - version = 3 3 + version = 4 4 + 5 + [[package]] 6 + name = "addr2line" 7 + version = "0.24.2" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 + dependencies = [ 11 + "gimli", 12 + ] 13 + 14 + [[package]] 15 + name = "adler2" 16 + version = "2.0.0" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 4 19 5 20 [[package]] 6 21 name = "aho-corasick" 7 - version = "0.7.19" 22 + version = "1.1.3" 8 23 source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 24 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 25 dependencies = [ 11 26 "memchr", 12 27 ] 13 28 14 29 [[package]] 30 + name = "anstream" 31 + version = "0.6.19" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" 34 + dependencies = [ 35 + "anstyle", 36 + "anstyle-parse", 37 + "anstyle-query", 38 + "anstyle-wincon", 39 + "colorchoice", 40 + "is_terminal_polyfill", 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle" 46 + version = "1.0.11" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 49 + 50 + [[package]] 51 + name = "anstyle-parse" 52 + version = "0.2.7" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 55 + dependencies = [ 56 + "utf8parse", 57 + ] 58 + 59 + [[package]] 60 + name = "anstyle-query" 61 + version = "1.1.3" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" 64 + dependencies = [ 65 + "windows-sys 0.59.0", 66 + ] 67 + 68 + [[package]] 69 + name = "anstyle-wincon" 70 + version = "3.0.9" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" 73 + dependencies = [ 74 + "anstyle", 75 + "once_cell_polyfill", 76 + "windows-sys 0.59.0", 77 + ] 78 + 79 + [[package]] 80 + name = "anyhow" 81 + version = "1.0.98" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 84 + 85 + [[package]] 15 86 name = "async-trait" 16 - version = "0.1.57" 87 + version = "0.1.88" 17 88 source = "registry+https://github.com/rust-lang/crates.io-index" 18 - checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" 89 + checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" 19 90 dependencies = [ 20 91 "proc-macro2", 21 92 "quote", ··· 23 94 ] 24 95 25 96 [[package]] 26 - name = "atty" 27 - version = "0.2.14" 97 + name = "autocfg" 98 + version = "1.4.0" 28 99 source = "registry+https://github.com/rust-lang/crates.io-index" 29 - checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 100 + checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 101 + 102 + [[package]] 103 + name = "backtrace" 104 + version = "0.3.75" 105 + source = "registry+https://github.com/rust-lang/crates.io-index" 106 + checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 30 107 dependencies = [ 31 - "hermit-abi", 108 + "addr2line", 109 + "cfg-if", 32 110 "libc", 33 - "winapi", 111 + "miniz_oxide", 112 + "object", 113 + "rustc-demangle", 114 + "windows-targets", 34 115 ] 35 116 36 117 [[package]] 37 - name = "autocfg" 38 - version = "1.1.0" 118 + name = "base64" 119 + version = "0.21.7" 39 120 source = "registry+https://github.com/rust-lang/crates.io-index" 40 - checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 121 + checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 41 122 42 123 [[package]] 43 124 name = "base64" 44 - version = "0.13.0" 125 + version = "0.22.1" 126 + source = "registry+https://github.com/rust-lang/crates.io-index" 127 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 128 + 129 + [[package]] 130 + name = "bb8" 131 + version = "0.8.6" 45 132 source = "registry+https://github.com/rust-lang/crates.io-index" 46 - checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 133 + checksum = "d89aabfae550a5c44b43ab941844ffcd2e993cb6900b342debf59e9ea74acdb8" 134 + dependencies = [ 135 + "async-trait", 136 + "futures-util", 137 + "parking_lot", 138 + "tokio", 139 + ] 47 140 48 141 [[package]] 49 142 name = "binascii" ··· 53 146 54 147 [[package]] 55 148 name = "bitflags" 56 - version = "1.3.2" 149 + version = "2.9.1" 57 150 source = "registry+https://github.com/rust-lang/crates.io-index" 58 - checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 151 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 59 152 60 153 [[package]] 61 154 name = "block-buffer" 62 - version = "0.10.3" 155 + version = "0.10.4" 63 156 source = "registry+https://github.com/rust-lang/crates.io-index" 64 - checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 157 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 65 158 dependencies = [ 66 159 "generic-array", 67 160 ] 68 161 69 162 [[package]] 70 - name = "buf_redux" 71 - version = "0.8.4" 163 + name = "bumpalo" 164 + version = "3.18.1" 72 165 source = "registry+https://github.com/rust-lang/crates.io-index" 73 - checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 74 - dependencies = [ 75 - "memchr", 76 - "safemem", 77 - ] 166 + checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" 78 167 79 168 [[package]] 80 169 name = "byteorder" 81 - version = "1.4.3" 170 + version = "1.5.0" 82 171 source = "registry+https://github.com/rust-lang/crates.io-index" 83 - checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 172 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 84 173 85 174 [[package]] 86 175 name = "bytes" 87 - version = "1.2.1" 176 + version = "1.10.1" 88 177 source = "registry+https://github.com/rust-lang/crates.io-index" 89 - checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 178 + checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 90 179 91 180 [[package]] 92 181 name = "cc" 93 - version = "1.0.73" 182 + version = "1.2.26" 94 183 source = "registry+https://github.com/rust-lang/crates.io-index" 95 - checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 184 + checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" 185 + dependencies = [ 186 + "shlex", 187 + ] 96 188 97 189 [[package]] 98 190 name = "cfg-if" ··· 102 194 103 195 [[package]] 104 196 name = "clap" 105 - version = "4.0.13" 197 + version = "4.5.39" 198 + source = "registry+https://github.com/rust-lang/crates.io-index" 199 + checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" 200 + dependencies = [ 201 + "clap_builder", 202 + ] 203 + 204 + [[package]] 205 + name = "clap_builder" 206 + version = "4.5.39" 106 207 source = "registry+https://github.com/rust-lang/crates.io-index" 107 - checksum = "69d64e88428747154bd8bc378d178377ef4dace7a5735ca1f3855be72f2c2cb5" 208 + checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" 108 209 dependencies = [ 109 - "atty", 110 - "bitflags", 210 + "anstream", 211 + "anstyle", 111 212 "clap_lex", 112 213 "strsim", 113 - "termcolor", 114 214 ] 115 215 116 216 [[package]] 117 217 name = "clap_lex" 118 - version = "0.3.0" 218 + version = "0.7.4" 219 + source = "registry+https://github.com/rust-lang/crates.io-index" 220 + checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 221 + 222 + [[package]] 223 + name = "colorchoice" 224 + version = "1.0.4" 119 225 source = "registry+https://github.com/rust-lang/crates.io-index" 120 - checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 121 - dependencies = [ 122 - "os_str_bytes", 123 - ] 226 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 124 227 125 228 [[package]] 126 229 name = "common" 127 230 version = "0.2.0" 128 231 dependencies = [ 232 + "anyhow", 129 233 "binascii", 234 + "diesel", 235 + "diesel-async", 130 236 "futures-util", 131 237 "log", 132 238 "native-tls", 133 239 "postgres-native-tls", 240 + "prometheus-client", 134 241 "serde", 242 + "time", 135 243 "tokio", 136 244 "tokio-postgres", 137 245 "toml", ··· 140 248 141 249 [[package]] 142 250 name = "core-foundation" 143 - version = "0.9.3" 251 + version = "0.9.4" 144 252 source = "registry+https://github.com/rust-lang/crates.io-index" 145 - checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 253 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 146 254 dependencies = [ 147 255 "core-foundation-sys", 148 256 "libc", ··· 150 258 151 259 [[package]] 152 260 name = "core-foundation-sys" 153 - version = "0.8.3" 261 + version = "0.8.7" 154 262 source = "registry+https://github.com/rust-lang/crates.io-index" 155 - checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 263 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 156 264 157 265 [[package]] 158 266 name = "cpufeatures" 159 - version = "0.2.5" 267 + version = "0.2.17" 160 268 source = "registry+https://github.com/rust-lang/crates.io-index" 161 - checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 269 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 162 270 dependencies = [ 163 271 "libc", 164 272 ] ··· 174 282 ] 175 283 176 284 [[package]] 285 + name = "darling" 286 + version = "0.20.11" 287 + source = "registry+https://github.com/rust-lang/crates.io-index" 288 + checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 289 + dependencies = [ 290 + "darling_core", 291 + "darling_macro", 292 + ] 293 + 294 + [[package]] 295 + name = "darling_core" 296 + version = "0.20.11" 297 + source = "registry+https://github.com/rust-lang/crates.io-index" 298 + checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 299 + dependencies = [ 300 + "fnv", 301 + "ident_case", 302 + "proc-macro2", 303 + "quote", 304 + "strsim", 305 + "syn", 306 + ] 307 + 308 + [[package]] 309 + name = "darling_macro" 310 + version = "0.20.11" 311 + source = "registry+https://github.com/rust-lang/crates.io-index" 312 + checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 313 + dependencies = [ 314 + "darling_core", 315 + "quote", 316 + "syn", 317 + ] 318 + 319 + [[package]] 320 + name = "data-encoding" 321 + version = "2.9.0" 322 + source = "registry+https://github.com/rust-lang/crates.io-index" 323 + checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 324 + 325 + [[package]] 326 + name = "deranged" 327 + version = "0.4.0" 328 + source = "registry+https://github.com/rust-lang/crates.io-index" 329 + checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 330 + dependencies = [ 331 + "powerfmt", 332 + ] 333 + 334 + [[package]] 335 + name = "diesel" 336 + version = "2.2.10" 337 + source = "registry+https://github.com/rust-lang/crates.io-index" 338 + checksum = "ff3e1edb1f37b4953dd5176916347289ed43d7119cc2e6c7c3f7849ff44ea506" 339 + dependencies = [ 340 + "bitflags", 341 + "byteorder", 342 + "diesel_derives", 343 + "itoa", 344 + "time", 345 + ] 346 + 347 + [[package]] 348 + name = "diesel-async" 349 + version = "0.5.2" 350 + source = "registry+https://github.com/rust-lang/crates.io-index" 351 + checksum = "51a307ac00f7c23f526a04a77761a0519b9f0eb2838ebf5b905a58580095bdcb" 352 + dependencies = [ 353 + "async-trait", 354 + "bb8", 355 + "diesel", 356 + "futures-util", 357 + "scoped-futures", 358 + "tokio", 359 + "tokio-postgres", 360 + ] 361 + 362 + [[package]] 363 + name = "diesel_derives" 364 + version = "2.2.5" 365 + source = "registry+https://github.com/rust-lang/crates.io-index" 366 + checksum = "68d4216021b3ea446fd2047f5c8f8fe6e98af34508a254a01e4d6bc1e844f84d" 367 + dependencies = [ 368 + "diesel_table_macro_syntax", 369 + "dsl_auto_type", 370 + "proc-macro2", 371 + "quote", 372 + "syn", 373 + ] 374 + 375 + [[package]] 376 + name = "diesel_table_macro_syntax" 377 + version = "0.2.0" 378 + source = "registry+https://github.com/rust-lang/crates.io-index" 379 + checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" 380 + dependencies = [ 381 + "syn", 382 + ] 383 + 384 + [[package]] 177 385 name = "digest" 178 - version = "0.10.5" 386 + version = "0.10.7" 179 387 source = "registry+https://github.com/rust-lang/crates.io-index" 180 - checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" 388 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 181 389 dependencies = [ 182 390 "block-buffer", 183 391 "crypto-common", ··· 185 393 ] 186 394 187 395 [[package]] 396 + name = "displaydoc" 397 + version = "0.2.5" 398 + source = "registry+https://github.com/rust-lang/crates.io-index" 399 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 400 + dependencies = [ 401 + "proc-macro2", 402 + "quote", 403 + "syn", 404 + ] 405 + 406 + [[package]] 407 + name = "dsl_auto_type" 408 + version = "0.1.3" 409 + source = "registry+https://github.com/rust-lang/crates.io-index" 410 + checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" 411 + dependencies = [ 412 + "darling", 413 + "either", 414 + "heck", 415 + "proc-macro2", 416 + "quote", 417 + "syn", 418 + ] 419 + 420 + [[package]] 421 + name = "dtoa" 422 + version = "1.0.10" 423 + source = "registry+https://github.com/rust-lang/crates.io-index" 424 + checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" 425 + 426 + [[package]] 427 + name = "either" 428 + version = "1.15.0" 429 + source = "registry+https://github.com/rust-lang/crates.io-index" 430 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 431 + 432 + [[package]] 433 + name = "encoding_rs" 434 + version = "0.8.35" 435 + source = "registry+https://github.com/rust-lang/crates.io-index" 436 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 437 + dependencies = [ 438 + "cfg-if", 439 + ] 440 + 441 + [[package]] 188 442 name = "env_logger" 189 - version = "0.7.1" 443 + version = "0.10.2" 190 444 source = "registry+https://github.com/rust-lang/crates.io-index" 191 - checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 445 + checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" 192 446 dependencies = [ 193 - "atty", 194 447 "humantime", 448 + "is-terminal", 195 449 "log", 196 450 "regex", 197 451 "termcolor", 198 452 ] 199 453 200 454 [[package]] 455 + name = "equivalent" 456 + version = "1.0.2" 457 + source = "registry+https://github.com/rust-lang/crates.io-index" 458 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 459 + 460 + [[package]] 461 + name = "errno" 462 + version = "0.3.12" 463 + source = "registry+https://github.com/rust-lang/crates.io-index" 464 + checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" 465 + dependencies = [ 466 + "libc", 467 + "windows-sys 0.59.0", 468 + ] 469 + 470 + [[package]] 201 471 name = "fallible-iterator" 202 472 version = "0.2.0" 203 473 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 205 475 206 476 [[package]] 207 477 name = "fastrand" 208 - version = "1.8.0" 478 + version = "2.3.0" 209 479 source = "registry+https://github.com/rust-lang/crates.io-index" 210 - checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 211 - dependencies = [ 212 - "instant", 213 - ] 480 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 214 481 215 482 [[package]] 216 483 name = "fnv" ··· 235 502 236 503 [[package]] 237 504 name = "form_urlencoded" 238 - version = "1.1.0" 505 + version = "1.2.1" 239 506 source = "registry+https://github.com/rust-lang/crates.io-index" 240 - checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 507 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 241 508 dependencies = [ 242 509 "percent-encoding", 243 510 ] 244 511 245 512 [[package]] 246 - name = "futures" 247 - version = "0.3.24" 248 - source = "registry+https://github.com/rust-lang/crates.io-index" 249 - checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" 250 - dependencies = [ 251 - "futures-channel", 252 - "futures-core", 253 - "futures-executor", 254 - "futures-io", 255 - "futures-sink", 256 - "futures-task", 257 - "futures-util", 258 - ] 259 - 260 - [[package]] 261 513 name = "futures-channel" 262 - version = "0.3.24" 514 + version = "0.3.31" 263 515 source = "registry+https://github.com/rust-lang/crates.io-index" 264 - checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 516 + checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 265 517 dependencies = [ 266 518 "futures-core", 267 519 "futures-sink", ··· 269 521 270 522 [[package]] 271 523 name = "futures-core" 272 - version = "0.3.24" 273 - source = "registry+https://github.com/rust-lang/crates.io-index" 274 - checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" 275 - 276 - [[package]] 277 - name = "futures-executor" 278 - version = "0.3.24" 279 - source = "registry+https://github.com/rust-lang/crates.io-index" 280 - checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" 281 - dependencies = [ 282 - "futures-core", 283 - "futures-task", 284 - "futures-util", 285 - ] 286 - 287 - [[package]] 288 - name = "futures-io" 289 - version = "0.3.24" 524 + version = "0.3.31" 290 525 source = "registry+https://github.com/rust-lang/crates.io-index" 291 - checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" 526 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 292 527 293 528 [[package]] 294 529 name = "futures-macro" 295 - version = "0.3.24" 530 + version = "0.3.31" 296 531 source = "registry+https://github.com/rust-lang/crates.io-index" 297 - checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" 532 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 298 533 dependencies = [ 299 534 "proc-macro2", 300 535 "quote", ··· 303 538 304 539 [[package]] 305 540 name = "futures-sink" 306 - version = "0.3.24" 541 + version = "0.3.31" 307 542 source = "registry+https://github.com/rust-lang/crates.io-index" 308 - checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 543 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 309 544 310 545 [[package]] 311 546 name = "futures-task" 312 - version = "0.3.24" 547 + version = "0.3.31" 313 548 source = "registry+https://github.com/rust-lang/crates.io-index" 314 - checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 549 + checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 315 550 316 551 [[package]] 317 552 name = "futures-util" 318 - version = "0.3.24" 553 + version = "0.3.31" 319 554 source = "registry+https://github.com/rust-lang/crates.io-index" 320 - checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 555 + checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 321 556 dependencies = [ 322 - "futures-channel", 323 557 "futures-core", 324 - "futures-io", 325 558 "futures-macro", 326 559 "futures-sink", 327 560 "futures-task", 328 - "memchr", 329 561 "pin-project-lite", 330 562 "pin-utils", 331 563 "slab", ··· 333 565 334 566 [[package]] 335 567 name = "generic-array" 336 - version = "0.14.6" 568 + version = "0.14.7" 337 569 source = "registry+https://github.com/rust-lang/crates.io-index" 338 - checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 570 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 339 571 dependencies = [ 340 572 "typenum", 341 573 "version_check", ··· 343 575 344 576 [[package]] 345 577 name = "getrandom" 346 - version = "0.2.7" 578 + version = "0.2.16" 347 579 source = "registry+https://github.com/rust-lang/crates.io-index" 348 - checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 580 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 349 581 dependencies = [ 350 582 "cfg-if", 351 583 "libc", 352 - "wasi", 584 + "wasi 0.11.0+wasi-snapshot-preview1", 353 585 ] 354 586 355 587 [[package]] 588 + name = "getrandom" 589 + version = "0.3.3" 590 + source = "registry+https://github.com/rust-lang/crates.io-index" 591 + checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 592 + dependencies = [ 593 + "cfg-if", 594 + "libc", 595 + "r-efi", 596 + "wasi 0.14.2+wasi-0.2.4", 597 + ] 598 + 599 + [[package]] 600 + name = "gimli" 601 + version = "0.31.1" 602 + source = "registry+https://github.com/rust-lang/crates.io-index" 603 + checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 604 + 605 + [[package]] 356 606 name = "h2" 357 - version = "0.3.14" 607 + version = "0.3.26" 358 608 source = "registry+https://github.com/rust-lang/crates.io-index" 359 - checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" 609 + checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 360 610 dependencies = [ 361 611 "bytes", 362 612 "fnv", 363 613 "futures-core", 364 614 "futures-sink", 365 615 "futures-util", 366 - "http", 616 + "http 0.2.12", 367 617 "indexmap", 368 618 "slab", 369 619 "tokio", ··· 373 623 374 624 [[package]] 375 625 name = "hashbrown" 376 - version = "0.12.3" 626 + version = "0.15.4" 377 627 source = "registry+https://github.com/rust-lang/crates.io-index" 378 - checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 628 + checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 379 629 380 630 [[package]] 381 631 name = "headers" 382 - version = "0.3.8" 632 + version = "0.3.9" 383 633 source = "registry+https://github.com/rust-lang/crates.io-index" 384 - checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" 634 + checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" 385 635 dependencies = [ 386 - "base64", 387 - "bitflags", 636 + "base64 0.21.7", 388 637 "bytes", 389 638 "headers-core", 390 - "http", 639 + "http 0.2.12", 391 640 "httpdate", 392 641 "mime", 393 642 "sha1", ··· 399 648 source = "registry+https://github.com/rust-lang/crates.io-index" 400 649 checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" 401 650 dependencies = [ 402 - "http", 651 + "http 0.2.12", 403 652 ] 404 653 405 654 [[package]] 655 + name = "heck" 656 + version = "0.5.0" 657 + source = "registry+https://github.com/rust-lang/crates.io-index" 658 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 659 + 660 + [[package]] 406 661 name = "hermit-abi" 407 - version = "0.1.19" 662 + version = "0.5.1" 408 663 source = "registry+https://github.com/rust-lang/crates.io-index" 409 - checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 410 - dependencies = [ 411 - "libc", 412 - ] 664 + checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" 413 665 414 666 [[package]] 415 667 name = "hmac" ··· 422 674 423 675 [[package]] 424 676 name = "http" 425 - version = "0.2.8" 677 + version = "0.2.12" 426 678 source = "registry+https://github.com/rust-lang/crates.io-index" 427 - checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 679 + checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 680 + dependencies = [ 681 + "bytes", 682 + "fnv", 683 + "itoa", 684 + ] 685 + 686 + [[package]] 687 + name = "http" 688 + version = "1.3.1" 689 + source = "registry+https://github.com/rust-lang/crates.io-index" 690 + checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 428 691 dependencies = [ 429 692 "bytes", 430 693 "fnv", ··· 433 696 434 697 [[package]] 435 698 name = "http-body" 436 - version = "0.4.5" 699 + version = "0.4.6" 437 700 source = "registry+https://github.com/rust-lang/crates.io-index" 438 - checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 701 + checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 439 702 dependencies = [ 440 703 "bytes", 441 - "http", 704 + "http 0.2.12", 442 705 "pin-project-lite", 443 706 ] 444 707 445 708 [[package]] 446 709 name = "httparse" 447 - version = "1.8.0" 710 + version = "1.10.1" 448 711 source = "registry+https://github.com/rust-lang/crates.io-index" 449 - checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 712 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 450 713 451 714 [[package]] 452 715 name = "httpdate" 453 - version = "1.0.2" 716 + version = "1.0.3" 454 717 source = "registry+https://github.com/rust-lang/crates.io-index" 455 - checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 718 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 456 719 457 720 [[package]] 458 721 name = "humantime" 459 - version = "1.3.0" 722 + version = "2.2.0" 460 723 source = "registry+https://github.com/rust-lang/crates.io-index" 461 - checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 462 - dependencies = [ 463 - "quick-error", 464 - ] 724 + checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" 465 725 466 726 [[package]] 467 727 name = "hyper" 468 - version = "0.14.20" 728 + version = "0.14.32" 469 729 source = "registry+https://github.com/rust-lang/crates.io-index" 470 - checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 730 + checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" 471 731 dependencies = [ 472 732 "bytes", 473 733 "futures-channel", 474 734 "futures-core", 475 735 "futures-util", 476 736 "h2", 477 - "http", 737 + "http 0.2.12", 478 738 "http-body", 479 739 "httparse", 480 740 "httpdate", ··· 488 748 ] 489 749 490 750 [[package]] 751 + name = "icu_collections" 752 + version = "2.0.0" 753 + source = "registry+https://github.com/rust-lang/crates.io-index" 754 + checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 755 + dependencies = [ 756 + "displaydoc", 757 + "potential_utf", 758 + "yoke", 759 + "zerofrom", 760 + "zerovec", 761 + ] 762 + 763 + [[package]] 764 + name = "icu_locale_core" 765 + version = "2.0.0" 766 + source = "registry+https://github.com/rust-lang/crates.io-index" 767 + checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 768 + dependencies = [ 769 + "displaydoc", 770 + "litemap", 771 + "tinystr", 772 + "writeable", 773 + "zerovec", 774 + ] 775 + 776 + [[package]] 777 + name = "icu_normalizer" 778 + version = "2.0.0" 779 + source = "registry+https://github.com/rust-lang/crates.io-index" 780 + checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 781 + dependencies = [ 782 + "displaydoc", 783 + "icu_collections", 784 + "icu_normalizer_data", 785 + "icu_properties", 786 + "icu_provider", 787 + "smallvec", 788 + "zerovec", 789 + ] 790 + 791 + [[package]] 792 + name = "icu_normalizer_data" 793 + version = "2.0.0" 794 + source = "registry+https://github.com/rust-lang/crates.io-index" 795 + checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 796 + 797 + [[package]] 798 + name = "icu_properties" 799 + version = "2.0.1" 800 + source = "registry+https://github.com/rust-lang/crates.io-index" 801 + checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 802 + dependencies = [ 803 + "displaydoc", 804 + "icu_collections", 805 + "icu_locale_core", 806 + "icu_properties_data", 807 + "icu_provider", 808 + "potential_utf", 809 + "zerotrie", 810 + "zerovec", 811 + ] 812 + 813 + [[package]] 814 + name = "icu_properties_data" 815 + version = "2.0.1" 816 + source = "registry+https://github.com/rust-lang/crates.io-index" 817 + checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 818 + 819 + [[package]] 820 + name = "icu_provider" 821 + version = "2.0.0" 822 + source = "registry+https://github.com/rust-lang/crates.io-index" 823 + checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 824 + dependencies = [ 825 + "displaydoc", 826 + "icu_locale_core", 827 + "stable_deref_trait", 828 + "tinystr", 829 + "writeable", 830 + "yoke", 831 + "zerofrom", 832 + "zerotrie", 833 + "zerovec", 834 + ] 835 + 836 + [[package]] 837 + name = "ident_case" 838 + version = "1.0.1" 839 + source = "registry+https://github.com/rust-lang/crates.io-index" 840 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 841 + 842 + [[package]] 491 843 name = "idna" 492 - version = "0.3.0" 844 + version = "1.0.3" 845 + source = "registry+https://github.com/rust-lang/crates.io-index" 846 + checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 847 + dependencies = [ 848 + "idna_adapter", 849 + "smallvec", 850 + "utf8_iter", 851 + ] 852 + 853 + [[package]] 854 + name = "idna_adapter" 855 + version = "1.2.1" 493 856 source = "registry+https://github.com/rust-lang/crates.io-index" 494 - checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 857 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 495 858 dependencies = [ 496 - "unicode-bidi", 497 - "unicode-normalization", 859 + "icu_normalizer", 860 + "icu_properties", 498 861 ] 499 862 500 863 [[package]] 501 864 name = "indexmap" 502 - version = "1.9.1" 865 + version = "2.9.0" 503 866 source = "registry+https://github.com/rust-lang/crates.io-index" 504 - checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 867 + checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 505 868 dependencies = [ 506 - "autocfg", 869 + "equivalent", 507 870 "hashbrown", 508 871 ] 509 872 510 873 [[package]] 511 - name = "instant" 512 - version = "0.1.12" 874 + name = "is-terminal" 875 + version = "0.4.16" 513 876 source = "registry+https://github.com/rust-lang/crates.io-index" 514 - checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 877 + checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" 515 878 dependencies = [ 516 - "cfg-if", 879 + "hermit-abi", 880 + "libc", 881 + "windows-sys 0.59.0", 517 882 ] 518 883 519 884 [[package]] 885 + name = "is_terminal_polyfill" 886 + version = "1.70.1" 887 + source = "registry+https://github.com/rust-lang/crates.io-index" 888 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 889 + 890 + [[package]] 520 891 name = "itoa" 521 - version = "1.0.4" 892 + version = "1.0.15" 522 893 source = "registry+https://github.com/rust-lang/crates.io-index" 523 - checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 894 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 524 895 525 896 [[package]] 526 - name = "lazy_static" 527 - version = "1.4.0" 897 + name = "js-sys" 898 + version = "0.3.77" 528 899 source = "registry+https://github.com/rust-lang/crates.io-index" 529 - checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 900 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 901 + dependencies = [ 902 + "once_cell", 903 + "wasm-bindgen", 904 + ] 530 905 531 906 [[package]] 532 907 name = "libc" 533 - version = "0.2.135" 908 + version = "0.2.172" 909 + source = "registry+https://github.com/rust-lang/crates.io-index" 910 + checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 911 + 912 + [[package]] 913 + name = "linux-raw-sys" 914 + version = "0.9.4" 534 915 source = "registry+https://github.com/rust-lang/crates.io-index" 535 - checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 916 + checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 917 + 918 + [[package]] 919 + name = "litemap" 920 + version = "0.8.0" 921 + source = "registry+https://github.com/rust-lang/crates.io-index" 922 + checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 536 923 537 924 [[package]] 538 925 name = "lock_api" 539 - version = "0.4.9" 926 + version = "0.4.13" 540 927 source = "registry+https://github.com/rust-lang/crates.io-index" 541 - checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 928 + checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 542 929 dependencies = [ 543 930 "autocfg", 544 931 "scopeguard", ··· 546 933 547 934 [[package]] 548 935 name = "log" 549 - version = "0.4.17" 936 + version = "0.4.27" 550 937 source = "registry+https://github.com/rust-lang/crates.io-index" 551 - checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 552 - dependencies = [ 553 - "cfg-if", 554 - ] 938 + checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 555 939 556 940 [[package]] 557 941 name = "lumen" ··· 562 946 "log", 563 947 "native-tls", 564 948 "pretty_env_logger", 949 + "prometheus-client", 565 950 "tokio", 566 951 "tokio-native-tls", 567 952 "warp", ··· 569 954 570 955 [[package]] 571 956 name = "md-5" 572 - version = "0.10.5" 957 + version = "0.10.6" 573 958 source = "registry+https://github.com/rust-lang/crates.io-index" 574 - checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" 959 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 575 960 dependencies = [ 961 + "cfg-if", 576 962 "digest", 577 963 ] 578 964 579 965 [[package]] 580 966 name = "memchr" 581 - version = "2.5.0" 967 + version = "2.7.4" 582 968 source = "registry+https://github.com/rust-lang/crates.io-index" 583 - checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 969 + checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 584 970 585 971 [[package]] 586 972 name = "mime" 587 - version = "0.3.16" 973 + version = "0.3.17" 588 974 source = "registry+https://github.com/rust-lang/crates.io-index" 589 - checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 975 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 590 976 591 977 [[package]] 592 978 name = "mime_guess" 593 - version = "2.0.4" 979 + version = "2.0.5" 594 980 source = "registry+https://github.com/rust-lang/crates.io-index" 595 - checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" 981 + checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" 596 982 dependencies = [ 597 983 "mime", 598 984 "unicase", 599 985 ] 600 986 601 987 [[package]] 988 + name = "miniz_oxide" 989 + version = "0.8.8" 990 + source = "registry+https://github.com/rust-lang/crates.io-index" 991 + checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 992 + dependencies = [ 993 + "adler2", 994 + ] 995 + 996 + [[package]] 602 997 name = "mio" 603 - version = "0.8.4" 998 + version = "1.0.4" 604 999 source = "registry+https://github.com/rust-lang/crates.io-index" 605 - checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 1000 + checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 606 1001 dependencies = [ 607 1002 "libc", 608 - "log", 609 - "wasi", 610 - "windows-sys", 1003 + "wasi 0.11.0+wasi-snapshot-preview1", 1004 + "windows-sys 0.59.0", 611 1005 ] 612 1006 613 1007 [[package]] 614 - name = "multipart" 615 - version = "0.18.0" 1008 + name = "multer" 1009 + version = "2.1.0" 616 1010 source = "registry+https://github.com/rust-lang/crates.io-index" 617 - checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 1011 + checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" 618 1012 dependencies = [ 619 - "buf_redux", 1013 + "bytes", 1014 + "encoding_rs", 1015 + "futures-util", 1016 + "http 0.2.12", 620 1017 "httparse", 621 1018 "log", 1019 + "memchr", 622 1020 "mime", 623 - "mime_guess", 624 - "quick-error", 625 - "rand", 626 - "safemem", 627 - "tempfile", 628 - "twoway", 1021 + "spin", 1022 + "version_check", 629 1023 ] 630 1024 631 1025 [[package]] 632 1026 name = "native-tls" 633 - version = "0.2.10" 1027 + version = "0.2.14" 634 1028 source = "registry+https://github.com/rust-lang/crates.io-index" 635 - checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" 1029 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 636 1030 dependencies = [ 637 - "lazy_static", 638 1031 "libc", 639 1032 "log", 640 1033 "openssl", ··· 647 1040 ] 648 1041 649 1042 [[package]] 650 - name = "num_cpus" 651 - version = "1.13.1" 1043 + name = "num-conv" 1044 + version = "0.1.0" 1045 + source = "registry+https://github.com/rust-lang/crates.io-index" 1046 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1047 + 1048 + [[package]] 1049 + name = "object" 1050 + version = "0.36.7" 652 1051 source = "registry+https://github.com/rust-lang/crates.io-index" 653 - checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 1052 + checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 654 1053 dependencies = [ 655 - "hermit-abi", 656 - "libc", 1054 + "memchr", 657 1055 ] 658 1056 659 1057 [[package]] 660 1058 name = "once_cell" 661 - version = "1.15.0" 1059 + version = "1.21.3" 1060 + source = "registry+https://github.com/rust-lang/crates.io-index" 1061 + checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1062 + 1063 + [[package]] 1064 + name = "once_cell_polyfill" 1065 + version = "1.70.1" 662 1066 source = "registry+https://github.com/rust-lang/crates.io-index" 663 - checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 1067 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 664 1068 665 1069 [[package]] 666 1070 name = "openssl" 667 - version = "0.10.42" 1071 + version = "0.10.73" 668 1072 source = "registry+https://github.com/rust-lang/crates.io-index" 669 - checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" 1073 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 670 1074 dependencies = [ 671 1075 "bitflags", 672 1076 "cfg-if", ··· 679 1083 680 1084 [[package]] 681 1085 name = "openssl-macros" 682 - version = "0.1.0" 1086 + version = "0.1.1" 683 1087 source = "registry+https://github.com/rust-lang/crates.io-index" 684 - checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 1088 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 685 1089 dependencies = [ 686 1090 "proc-macro2", 687 1091 "quote", ··· 690 1094 691 1095 [[package]] 692 1096 name = "openssl-probe" 693 - version = "0.1.5" 1097 + version = "0.1.6" 694 1098 source = "registry+https://github.com/rust-lang/crates.io-index" 695 - checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1099 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 696 1100 697 1101 [[package]] 698 1102 name = "openssl-sys" 699 - version = "0.9.76" 1103 + version = "0.9.109" 700 1104 source = "registry+https://github.com/rust-lang/crates.io-index" 701 - checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" 1105 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 702 1106 dependencies = [ 703 - "autocfg", 704 1107 "cc", 705 1108 "libc", 706 1109 "pkg-config", 707 1110 "vcpkg", 708 1111 ] 709 - 710 - [[package]] 711 - name = "os_str_bytes" 712 - version = "6.3.0" 713 - source = "registry+https://github.com/rust-lang/crates.io-index" 714 - checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 715 1112 716 1113 [[package]] 717 1114 name = "parking_lot" 718 - version = "0.12.1" 1115 + version = "0.12.4" 719 1116 source = "registry+https://github.com/rust-lang/crates.io-index" 720 - checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1117 + checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 721 1118 dependencies = [ 722 1119 "lock_api", 723 1120 "parking_lot_core", ··· 725 1122 726 1123 [[package]] 727 1124 name = "parking_lot_core" 728 - version = "0.9.3" 1125 + version = "0.9.11" 729 1126 source = "registry+https://github.com/rust-lang/crates.io-index" 730 - checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 1127 + checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 731 1128 dependencies = [ 732 1129 "cfg-if", 733 1130 "libc", 734 1131 "redox_syscall", 735 1132 "smallvec", 736 - "windows-sys", 1133 + "windows-targets", 737 1134 ] 738 1135 739 1136 [[package]] 740 1137 name = "percent-encoding" 741 - version = "2.2.0" 1138 + version = "2.3.1" 742 1139 source = "registry+https://github.com/rust-lang/crates.io-index" 743 - checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 1140 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 744 1141 745 1142 [[package]] 746 1143 name = "phf" 747 - version = "0.11.1" 1144 + version = "0.11.3" 748 1145 source = "registry+https://github.com/rust-lang/crates.io-index" 749 - checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" 1146 + checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 750 1147 dependencies = [ 751 1148 "phf_shared", 752 1149 ] 753 1150 754 1151 [[package]] 755 1152 name = "phf_shared" 756 - version = "0.11.1" 1153 + version = "0.11.3" 757 1154 source = "registry+https://github.com/rust-lang/crates.io-index" 758 - checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" 1155 + checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 759 1156 dependencies = [ 760 1157 "siphasher", 761 1158 ] 762 1159 763 1160 [[package]] 764 1161 name = "pin-project" 765 - version = "1.0.12" 1162 + version = "1.1.10" 766 1163 source = "registry+https://github.com/rust-lang/crates.io-index" 767 - checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 1164 + checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 768 1165 dependencies = [ 769 1166 "pin-project-internal", 770 1167 ] 771 1168 772 1169 [[package]] 773 1170 name = "pin-project-internal" 774 - version = "1.0.12" 1171 + version = "1.1.10" 775 1172 source = "registry+https://github.com/rust-lang/crates.io-index" 776 - checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 1173 + checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 777 1174 dependencies = [ 778 1175 "proc-macro2", 779 1176 "quote", ··· 782 1179 783 1180 [[package]] 784 1181 name = "pin-project-lite" 785 - version = "0.2.9" 1182 + version = "0.2.16" 786 1183 source = "registry+https://github.com/rust-lang/crates.io-index" 787 - checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 1184 + checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 788 1185 789 1186 [[package]] 790 1187 name = "pin-utils" ··· 794 1191 795 1192 [[package]] 796 1193 name = "pkg-config" 797 - version = "0.3.25" 1194 + version = "0.3.32" 798 1195 source = "registry+https://github.com/rust-lang/crates.io-index" 799 - checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 1196 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 800 1197 801 1198 [[package]] 802 1199 name = "postgres-native-tls" 803 - version = "0.5.0" 1200 + version = "0.5.1" 804 1201 source = "registry+https://github.com/rust-lang/crates.io-index" 805 - checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" 1202 + checksum = "a1f39498473c92f7b6820ae970382c1d83178a3454c618161cb772e8598d9f6f" 806 1203 dependencies = [ 807 - "futures", 808 1204 "native-tls", 809 1205 "tokio", 810 1206 "tokio-native-tls", ··· 813 1209 814 1210 [[package]] 815 1211 name = "postgres-protocol" 816 - version = "0.6.4" 1212 + version = "0.6.8" 817 1213 source = "registry+https://github.com/rust-lang/crates.io-index" 818 - checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" 1214 + checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" 819 1215 dependencies = [ 820 - "base64", 1216 + "base64 0.22.1", 821 1217 "byteorder", 822 1218 "bytes", 823 1219 "fallible-iterator", 824 1220 "hmac", 825 1221 "md-5", 826 1222 "memchr", 827 - "rand", 1223 + "rand 0.9.1", 828 1224 "sha2", 829 1225 "stringprep", 830 1226 ] 831 1227 832 1228 [[package]] 833 1229 name = "postgres-types" 834 - version = "0.2.4" 1230 + version = "0.2.9" 835 1231 source = "registry+https://github.com/rust-lang/crates.io-index" 836 - checksum = "73d946ec7d256b04dfadc4e6a3292324e6f417124750fc5c0950f981b703a0f1" 1232 + checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" 837 1233 dependencies = [ 838 1234 "bytes", 839 1235 "fallible-iterator", ··· 841 1237 ] 842 1238 843 1239 [[package]] 1240 + name = "potential_utf" 1241 + version = "0.1.2" 1242 + source = "registry+https://github.com/rust-lang/crates.io-index" 1243 + checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1244 + dependencies = [ 1245 + "zerovec", 1246 + ] 1247 + 1248 + [[package]] 1249 + name = "powerfmt" 1250 + version = "0.2.0" 1251 + source = "registry+https://github.com/rust-lang/crates.io-index" 1252 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1253 + 1254 + [[package]] 844 1255 name = "ppv-lite86" 845 - version = "0.2.16" 1256 + version = "0.2.21" 846 1257 source = "registry+https://github.com/rust-lang/crates.io-index" 847 - checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 1258 + checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1259 + dependencies = [ 1260 + "zerocopy", 1261 + ] 848 1262 849 1263 [[package]] 850 1264 name = "pretty_env_logger" 851 - version = "0.4.0" 1265 + version = "0.5.0" 852 1266 source = "registry+https://github.com/rust-lang/crates.io-index" 853 - checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 1267 + checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" 854 1268 dependencies = [ 855 1269 "env_logger", 856 1270 "log", ··· 858 1272 859 1273 [[package]] 860 1274 name = "proc-macro2" 861 - version = "1.0.46" 1275 + version = "1.0.95" 862 1276 source = "registry+https://github.com/rust-lang/crates.io-index" 863 - checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 1277 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 864 1278 dependencies = [ 865 1279 "unicode-ident", 866 1280 ] 867 1281 868 1282 [[package]] 869 - name = "quick-error" 870 - version = "1.2.3" 1283 + name = "prometheus-client" 1284 + version = "0.22.3" 871 1285 source = "registry+https://github.com/rust-lang/crates.io-index" 872 - checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1286 + checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" 1287 + dependencies = [ 1288 + "dtoa", 1289 + "itoa", 1290 + "parking_lot", 1291 + "prometheus-client-derive-encode", 1292 + ] 1293 + 1294 + [[package]] 1295 + name = "prometheus-client-derive-encode" 1296 + version = "0.4.2" 1297 + source = "registry+https://github.com/rust-lang/crates.io-index" 1298 + checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" 1299 + dependencies = [ 1300 + "proc-macro2", 1301 + "quote", 1302 + "syn", 1303 + ] 873 1304 874 1305 [[package]] 875 1306 name = "quote" 876 - version = "1.0.21" 1307 + version = "1.0.40" 877 1308 source = "registry+https://github.com/rust-lang/crates.io-index" 878 - checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 1309 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 879 1310 dependencies = [ 880 1311 "proc-macro2", 881 1312 ] 882 1313 883 1314 [[package]] 1315 + name = "r-efi" 1316 + version = "5.2.0" 1317 + source = "registry+https://github.com/rust-lang/crates.io-index" 1318 + checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1319 + 1320 + [[package]] 884 1321 name = "rand" 885 1322 version = "0.8.5" 886 1323 source = "registry+https://github.com/rust-lang/crates.io-index" 887 1324 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 888 1325 dependencies = [ 889 1326 "libc", 890 - "rand_chacha", 891 - "rand_core", 1327 + "rand_chacha 0.3.1", 1328 + "rand_core 0.6.4", 1329 + ] 1330 + 1331 + [[package]] 1332 + name = "rand" 1333 + version = "0.9.1" 1334 + source = "registry+https://github.com/rust-lang/crates.io-index" 1335 + checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 1336 + dependencies = [ 1337 + "rand_chacha 0.9.0", 1338 + "rand_core 0.9.3", 892 1339 ] 893 1340 894 1341 [[package]] ··· 898 1345 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 899 1346 dependencies = [ 900 1347 "ppv-lite86", 901 - "rand_core", 1348 + "rand_core 0.6.4", 1349 + ] 1350 + 1351 + [[package]] 1352 + name = "rand_chacha" 1353 + version = "0.9.0" 1354 + source = "registry+https://github.com/rust-lang/crates.io-index" 1355 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1356 + dependencies = [ 1357 + "ppv-lite86", 1358 + "rand_core 0.9.3", 902 1359 ] 903 1360 904 1361 [[package]] ··· 907 1364 source = "registry+https://github.com/rust-lang/crates.io-index" 908 1365 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 909 1366 dependencies = [ 910 - "getrandom", 1367 + "getrandom 0.2.16", 1368 + ] 1369 + 1370 + [[package]] 1371 + name = "rand_core" 1372 + version = "0.9.3" 1373 + source = "registry+https://github.com/rust-lang/crates.io-index" 1374 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1375 + dependencies = [ 1376 + "getrandom 0.3.3", 911 1377 ] 912 1378 913 1379 [[package]] 914 1380 name = "redox_syscall" 915 - version = "0.2.16" 1381 + version = "0.5.12" 916 1382 source = "registry+https://github.com/rust-lang/crates.io-index" 917 - checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 1383 + checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" 918 1384 dependencies = [ 919 1385 "bitflags", 920 1386 ] 921 1387 922 1388 [[package]] 923 1389 name = "regex" 924 - version = "1.6.0" 1390 + version = "1.11.1" 925 1391 source = "registry+https://github.com/rust-lang/crates.io-index" 926 - checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 1392 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1393 + dependencies = [ 1394 + "aho-corasick", 1395 + "memchr", 1396 + "regex-automata", 1397 + "regex-syntax", 1398 + ] 1399 + 1400 + [[package]] 1401 + name = "regex-automata" 1402 + version = "0.4.9" 1403 + source = "registry+https://github.com/rust-lang/crates.io-index" 1404 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 927 1405 dependencies = [ 928 1406 "aho-corasick", 929 1407 "memchr", ··· 932 1410 933 1411 [[package]] 934 1412 name = "regex-syntax" 935 - version = "0.6.27" 1413 + version = "0.8.5" 936 1414 source = "registry+https://github.com/rust-lang/crates.io-index" 937 - checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 1415 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 938 1416 939 1417 [[package]] 940 - name = "remove_dir_all" 941 - version = "0.5.3" 1418 + name = "rustc-demangle" 1419 + version = "0.1.24" 942 1420 source = "registry+https://github.com/rust-lang/crates.io-index" 943 - checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 944 - dependencies = [ 945 - "winapi", 946 - ] 1421 + checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 947 1422 948 1423 [[package]] 949 - name = "rustls-pemfile" 950 - version = "0.2.1" 1424 + name = "rustix" 1425 + version = "1.0.7" 951 1426 source = "registry+https://github.com/rust-lang/crates.io-index" 952 - checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 1427 + checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 953 1428 dependencies = [ 954 - "base64", 1429 + "bitflags", 1430 + "errno", 1431 + "libc", 1432 + "linux-raw-sys", 1433 + "windows-sys 0.59.0", 955 1434 ] 956 1435 957 1436 [[package]] 958 1437 name = "ryu" 959 - version = "1.0.11" 1438 + version = "1.0.20" 960 1439 source = "registry+https://github.com/rust-lang/crates.io-index" 961 - checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 1440 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 962 1441 963 1442 [[package]] 964 - name = "safemem" 965 - version = "0.3.3" 1443 + name = "schannel" 1444 + version = "0.1.27" 966 1445 source = "registry+https://github.com/rust-lang/crates.io-index" 967 - checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 1446 + checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1447 + dependencies = [ 1448 + "windows-sys 0.59.0", 1449 + ] 968 1450 969 1451 [[package]] 970 - name = "schannel" 971 - version = "0.1.20" 1452 + name = "scoped-futures" 1453 + version = "0.1.4" 972 1454 source = "registry+https://github.com/rust-lang/crates.io-index" 973 - checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" 1455 + checksum = "1b24aae2d0636530f359e9d5ef0c04669d11c5e756699b27a6a6d845d8329091" 974 1456 dependencies = [ 975 - "lazy_static", 976 - "windows-sys", 1457 + "pin-project-lite", 977 1458 ] 978 1459 979 1460 [[package]] 980 1461 name = "scoped-tls" 981 - version = "1.0.0" 1462 + version = "1.0.1" 982 1463 source = "registry+https://github.com/rust-lang/crates.io-index" 983 - checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 1464 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 984 1465 985 1466 [[package]] 986 1467 name = "scopeguard" 987 - version = "1.1.0" 1468 + version = "1.2.0" 988 1469 source = "registry+https://github.com/rust-lang/crates.io-index" 989 - checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1470 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 990 1471 991 1472 [[package]] 992 1473 name = "security-framework" 993 - version = "2.7.0" 1474 + version = "2.11.1" 994 1475 source = "registry+https://github.com/rust-lang/crates.io-index" 995 - checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" 1476 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 996 1477 dependencies = [ 997 1478 "bitflags", 998 1479 "core-foundation", ··· 1003 1484 1004 1485 [[package]] 1005 1486 name = "security-framework-sys" 1006 - version = "2.6.1" 1487 + version = "2.14.0" 1007 1488 source = "registry+https://github.com/rust-lang/crates.io-index" 1008 - checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 1489 + checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1009 1490 dependencies = [ 1010 1491 "core-foundation-sys", 1011 1492 "libc", ··· 1013 1494 1014 1495 [[package]] 1015 1496 name = "serde" 1016 - version = "1.0.145" 1497 + version = "1.0.219" 1017 1498 source = "registry+https://github.com/rust-lang/crates.io-index" 1018 - checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 1499 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1019 1500 dependencies = [ 1020 1501 "serde_derive", 1021 1502 ] 1022 1503 1023 1504 [[package]] 1024 1505 name = "serde_derive" 1025 - version = "1.0.145" 1506 + version = "1.0.219" 1026 1507 source = "registry+https://github.com/rust-lang/crates.io-index" 1027 - checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 1508 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1028 1509 dependencies = [ 1029 1510 "proc-macro2", 1030 1511 "quote", ··· 1033 1514 1034 1515 [[package]] 1035 1516 name = "serde_json" 1036 - version = "1.0.86" 1517 + version = "1.0.140" 1037 1518 source = "registry+https://github.com/rust-lang/crates.io-index" 1038 - checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" 1519 + checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1039 1520 dependencies = [ 1040 1521 "itoa", 1522 + "memchr", 1041 1523 "ryu", 1042 1524 "serde", 1043 1525 ] 1044 1526 1045 1527 [[package]] 1528 + name = "serde_spanned" 1529 + version = "0.6.9" 1530 + source = "registry+https://github.com/rust-lang/crates.io-index" 1531 + checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" 1532 + dependencies = [ 1533 + "serde", 1534 + ] 1535 + 1536 + [[package]] 1046 1537 name = "serde_urlencoded" 1047 1538 version = "0.7.1" 1048 1539 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1055 1546 ] 1056 1547 1057 1548 [[package]] 1058 - name = "sha-1" 1059 - version = "0.10.0" 1549 + name = "sha1" 1550 + version = "0.10.6" 1060 1551 source = "registry+https://github.com/rust-lang/crates.io-index" 1061 - checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" 1552 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1062 1553 dependencies = [ 1063 1554 "cfg-if", 1064 1555 "cpufeatures", ··· 1066 1557 ] 1067 1558 1068 1559 [[package]] 1069 - name = "sha1" 1070 - version = "0.10.5" 1560 + name = "sha2" 1561 + version = "0.10.9" 1071 1562 source = "registry+https://github.com/rust-lang/crates.io-index" 1072 - checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1563 + checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1073 1564 dependencies = [ 1074 1565 "cfg-if", 1075 1566 "cpufeatures", ··· 1077 1568 ] 1078 1569 1079 1570 [[package]] 1080 - name = "sha2" 1081 - version = "0.10.6" 1571 + name = "shlex" 1572 + version = "1.3.0" 1082 1573 source = "registry+https://github.com/rust-lang/crates.io-index" 1083 - checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" 1084 - dependencies = [ 1085 - "cfg-if", 1086 - "cpufeatures", 1087 - "digest", 1088 - ] 1574 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1089 1575 1090 1576 [[package]] 1091 1577 name = "signal-hook-registry" 1092 - version = "1.4.0" 1578 + version = "1.4.5" 1093 1579 source = "registry+https://github.com/rust-lang/crates.io-index" 1094 - checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1580 + checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" 1095 1581 dependencies = [ 1096 1582 "libc", 1097 1583 ] 1098 1584 1099 1585 [[package]] 1100 1586 name = "siphasher" 1101 - version = "0.3.10" 1587 + version = "1.0.1" 1102 1588 source = "registry+https://github.com/rust-lang/crates.io-index" 1103 - checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" 1589 + checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 1104 1590 1105 1591 [[package]] 1106 1592 name = "slab" 1107 - version = "0.4.7" 1593 + version = "0.4.9" 1108 1594 source = "registry+https://github.com/rust-lang/crates.io-index" 1109 - checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 1595 + checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1110 1596 dependencies = [ 1111 1597 "autocfg", 1112 1598 ] 1113 1599 1114 1600 [[package]] 1115 1601 name = "smallvec" 1116 - version = "1.10.0" 1602 + version = "1.15.1" 1117 1603 source = "registry+https://github.com/rust-lang/crates.io-index" 1118 - checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1604 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1119 1605 1120 1606 [[package]] 1121 1607 name = "socket2" 1122 - version = "0.4.7" 1608 + version = "0.5.10" 1123 1609 source = "registry+https://github.com/rust-lang/crates.io-index" 1124 - checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1610 + checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 1125 1611 dependencies = [ 1126 1612 "libc", 1127 - "winapi", 1613 + "windows-sys 0.52.0", 1128 1614 ] 1129 1615 1130 1616 [[package]] 1617 + name = "spin" 1618 + version = "0.9.8" 1619 + source = "registry+https://github.com/rust-lang/crates.io-index" 1620 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1621 + 1622 + [[package]] 1623 + name = "stable_deref_trait" 1624 + version = "1.2.0" 1625 + source = "registry+https://github.com/rust-lang/crates.io-index" 1626 + checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1627 + 1628 + [[package]] 1131 1629 name = "stringprep" 1132 - version = "0.1.2" 1630 + version = "0.1.5" 1133 1631 source = "registry+https://github.com/rust-lang/crates.io-index" 1134 - checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" 1632 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 1135 1633 dependencies = [ 1136 1634 "unicode-bidi", 1137 1635 "unicode-normalization", 1636 + "unicode-properties", 1138 1637 ] 1139 1638 1140 1639 [[package]] 1141 1640 name = "strsim" 1142 - version = "0.10.0" 1641 + version = "0.11.1" 1143 1642 source = "registry+https://github.com/rust-lang/crates.io-index" 1144 - checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1643 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1145 1644 1146 1645 [[package]] 1147 1646 name = "subtle" 1148 - version = "2.4.1" 1647 + version = "2.6.1" 1149 1648 source = "registry+https://github.com/rust-lang/crates.io-index" 1150 - checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1649 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1151 1650 1152 1651 [[package]] 1153 1652 name = "syn" 1154 - version = "1.0.102" 1653 + version = "2.0.101" 1155 1654 source = "registry+https://github.com/rust-lang/crates.io-index" 1156 - checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" 1655 + checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 1157 1656 dependencies = [ 1158 1657 "proc-macro2", 1159 1658 "quote", ··· 1161 1660 ] 1162 1661 1163 1662 [[package]] 1663 + name = "synstructure" 1664 + version = "0.13.2" 1665 + source = "registry+https://github.com/rust-lang/crates.io-index" 1666 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1667 + dependencies = [ 1668 + "proc-macro2", 1669 + "quote", 1670 + "syn", 1671 + ] 1672 + 1673 + [[package]] 1164 1674 name = "tempfile" 1165 - version = "3.3.0" 1675 + version = "3.20.0" 1166 1676 source = "registry+https://github.com/rust-lang/crates.io-index" 1167 - checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1677 + checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 1168 1678 dependencies = [ 1169 - "cfg-if", 1170 1679 "fastrand", 1171 - "libc", 1172 - "redox_syscall", 1173 - "remove_dir_all", 1174 - "winapi", 1680 + "getrandom 0.3.3", 1681 + "once_cell", 1682 + "rustix", 1683 + "windows-sys 0.59.0", 1175 1684 ] 1176 1685 1177 1686 [[package]] 1178 1687 name = "termcolor" 1179 - version = "1.1.3" 1688 + version = "1.4.1" 1180 1689 source = "registry+https://github.com/rust-lang/crates.io-index" 1181 - checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1690 + checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1182 1691 dependencies = [ 1183 1692 "winapi-util", 1184 1693 ] 1185 1694 1186 1695 [[package]] 1187 1696 name = "thiserror" 1188 - version = "1.0.37" 1697 + version = "1.0.69" 1189 1698 source = "registry+https://github.com/rust-lang/crates.io-index" 1190 - checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 1699 + checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1191 1700 dependencies = [ 1192 1701 "thiserror-impl", 1193 1702 ] 1194 1703 1195 1704 [[package]] 1196 1705 name = "thiserror-impl" 1197 - version = "1.0.37" 1706 + version = "1.0.69" 1198 1707 source = "registry+https://github.com/rust-lang/crates.io-index" 1199 - checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 1708 + checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1200 1709 dependencies = [ 1201 1710 "proc-macro2", 1202 1711 "quote", ··· 1204 1713 ] 1205 1714 1206 1715 [[package]] 1716 + name = "time" 1717 + version = "0.3.41" 1718 + source = "registry+https://github.com/rust-lang/crates.io-index" 1719 + checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1720 + dependencies = [ 1721 + "deranged", 1722 + "num-conv", 1723 + "powerfmt", 1724 + "serde", 1725 + "time-core", 1726 + "time-macros", 1727 + ] 1728 + 1729 + [[package]] 1730 + name = "time-core" 1731 + version = "0.1.4" 1732 + source = "registry+https://github.com/rust-lang/crates.io-index" 1733 + checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1734 + 1735 + [[package]] 1736 + name = "time-macros" 1737 + version = "0.2.22" 1738 + source = "registry+https://github.com/rust-lang/crates.io-index" 1739 + checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1740 + dependencies = [ 1741 + "num-conv", 1742 + "time-core", 1743 + ] 1744 + 1745 + [[package]] 1746 + name = "tinystr" 1747 + version = "0.8.1" 1748 + source = "registry+https://github.com/rust-lang/crates.io-index" 1749 + checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1750 + dependencies = [ 1751 + "displaydoc", 1752 + "zerovec", 1753 + ] 1754 + 1755 + [[package]] 1207 1756 name = "tinyvec" 1208 - version = "1.6.0" 1757 + version = "1.9.0" 1209 1758 source = "registry+https://github.com/rust-lang/crates.io-index" 1210 - checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1759 + checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 1211 1760 dependencies = [ 1212 1761 "tinyvec_macros", 1213 1762 ] 1214 1763 1215 1764 [[package]] 1216 1765 name = "tinyvec_macros" 1217 - version = "0.1.0" 1766 + version = "0.1.1" 1218 1767 source = "registry+https://github.com/rust-lang/crates.io-index" 1219 - checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1768 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1220 1769 1221 1770 [[package]] 1222 1771 name = "tokio" 1223 - version = "1.21.2" 1772 + version = "1.45.1" 1224 1773 source = "registry+https://github.com/rust-lang/crates.io-index" 1225 - checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 1774 + checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 1226 1775 dependencies = [ 1227 - "autocfg", 1776 + "backtrace", 1228 1777 "bytes", 1229 1778 "libc", 1230 - "memchr", 1231 1779 "mio", 1232 - "num_cpus", 1233 1780 "parking_lot", 1234 1781 "pin-project-lite", 1235 1782 "signal-hook-registry", 1236 1783 "socket2", 1237 1784 "tokio-macros", 1238 - "winapi", 1785 + "windows-sys 0.52.0", 1239 1786 ] 1240 1787 1241 1788 [[package]] 1242 1789 name = "tokio-macros" 1243 - version = "1.8.0" 1790 + version = "2.5.0" 1244 1791 source = "registry+https://github.com/rust-lang/crates.io-index" 1245 - checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 1792 + checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1246 1793 dependencies = [ 1247 1794 "proc-macro2", 1248 1795 "quote", ··· 1251 1798 1252 1799 [[package]] 1253 1800 name = "tokio-native-tls" 1254 - version = "0.3.0" 1801 + version = "0.3.1" 1255 1802 source = "registry+https://github.com/rust-lang/crates.io-index" 1256 - checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1803 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1257 1804 dependencies = [ 1258 1805 "native-tls", 1259 1806 "tokio", ··· 1261 1808 1262 1809 [[package]] 1263 1810 name = "tokio-postgres" 1264 - version = "0.7.7" 1811 + version = "0.7.13" 1265 1812 source = "registry+https://github.com/rust-lang/crates.io-index" 1266 - checksum = "29a12c1b3e0704ae7dfc25562629798b29c72e6b1d0a681b6f29ab4ae5e7f7bf" 1813 + checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" 1267 1814 dependencies = [ 1268 1815 "async-trait", 1269 1816 "byteorder", ··· 1278 1825 "pin-project-lite", 1279 1826 "postgres-protocol", 1280 1827 "postgres-types", 1828 + "rand 0.9.1", 1281 1829 "socket2", 1282 1830 "tokio", 1283 1831 "tokio-util", 1284 - ] 1285 - 1286 - [[package]] 1287 - name = "tokio-stream" 1288 - version = "0.1.11" 1289 - source = "registry+https://github.com/rust-lang/crates.io-index" 1290 - checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" 1291 - dependencies = [ 1292 - "futures-core", 1293 - "pin-project-lite", 1294 - "tokio", 1832 + "whoami", 1295 1833 ] 1296 1834 1297 1835 [[package]] 1298 1836 name = "tokio-tungstenite" 1299 - version = "0.17.2" 1837 + version = "0.21.0" 1300 1838 source = "registry+https://github.com/rust-lang/crates.io-index" 1301 - checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" 1839 + checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" 1302 1840 dependencies = [ 1303 1841 "futures-util", 1304 1842 "log", ··· 1308 1846 1309 1847 [[package]] 1310 1848 name = "tokio-util" 1311 - version = "0.7.4" 1849 + version = "0.7.15" 1312 1850 source = "registry+https://github.com/rust-lang/crates.io-index" 1313 - checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 1851 + checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 1314 1852 dependencies = [ 1315 1853 "bytes", 1316 1854 "futures-core", 1317 1855 "futures-sink", 1318 1856 "pin-project-lite", 1319 1857 "tokio", 1320 - "tracing", 1321 1858 ] 1322 1859 1323 1860 [[package]] 1324 1861 name = "toml" 1325 - version = "0.5.9" 1862 + version = "0.8.23" 1863 + source = "registry+https://github.com/rust-lang/crates.io-index" 1864 + checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" 1865 + dependencies = [ 1866 + "serde", 1867 + "serde_spanned", 1868 + "toml_datetime", 1869 + "toml_edit", 1870 + ] 1871 + 1872 + [[package]] 1873 + name = "toml_datetime" 1874 + version = "0.6.11" 1326 1875 source = "registry+https://github.com/rust-lang/crates.io-index" 1327 - checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1876 + checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" 1328 1877 dependencies = [ 1329 1878 "serde", 1330 1879 ] 1331 1880 1332 1881 [[package]] 1882 + name = "toml_edit" 1883 + version = "0.22.27" 1884 + source = "registry+https://github.com/rust-lang/crates.io-index" 1885 + checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" 1886 + dependencies = [ 1887 + "indexmap", 1888 + "serde", 1889 + "serde_spanned", 1890 + "toml_datetime", 1891 + "toml_write", 1892 + "winnow", 1893 + ] 1894 + 1895 + [[package]] 1896 + name = "toml_write" 1897 + version = "0.1.2" 1898 + source = "registry+https://github.com/rust-lang/crates.io-index" 1899 + checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" 1900 + 1901 + [[package]] 1333 1902 name = "tower-service" 1334 - version = "0.3.2" 1903 + version = "0.3.3" 1335 1904 source = "registry+https://github.com/rust-lang/crates.io-index" 1336 - checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1905 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1337 1906 1338 1907 [[package]] 1339 1908 name = "tracing" 1340 - version = "0.1.37" 1909 + version = "0.1.41" 1341 1910 source = "registry+https://github.com/rust-lang/crates.io-index" 1342 - checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1911 + checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1343 1912 dependencies = [ 1344 - "cfg-if", 1345 1913 "log", 1346 1914 "pin-project-lite", 1347 1915 "tracing-core", ··· 1349 1917 1350 1918 [[package]] 1351 1919 name = "tracing-core" 1352 - version = "0.1.30" 1920 + version = "0.1.34" 1353 1921 source = "registry+https://github.com/rust-lang/crates.io-index" 1354 - checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1922 + checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 1355 1923 dependencies = [ 1356 1924 "once_cell", 1357 1925 ] 1358 1926 1359 1927 [[package]] 1360 1928 name = "try-lock" 1361 - version = "0.2.3" 1929 + version = "0.2.5" 1362 1930 source = "registry+https://github.com/rust-lang/crates.io-index" 1363 - checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1931 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1364 1932 1365 1933 [[package]] 1366 1934 name = "tungstenite" 1367 - version = "0.17.3" 1935 + version = "0.21.0" 1368 1936 source = "registry+https://github.com/rust-lang/crates.io-index" 1369 - checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" 1937 + checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" 1370 1938 dependencies = [ 1371 - "base64", 1372 1939 "byteorder", 1373 1940 "bytes", 1374 - "http", 1941 + "data-encoding", 1942 + "http 1.3.1", 1375 1943 "httparse", 1376 1944 "log", 1377 - "rand", 1378 - "sha-1", 1945 + "rand 0.8.5", 1946 + "sha1", 1379 1947 "thiserror", 1380 1948 "url", 1381 1949 "utf-8", 1382 - ] 1383 - 1384 - [[package]] 1385 - name = "twoway" 1386 - version = "0.1.8" 1387 - source = "registry+https://github.com/rust-lang/crates.io-index" 1388 - checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" 1389 - dependencies = [ 1390 - "memchr", 1391 1950 ] 1392 1951 1393 1952 [[package]] 1394 1953 name = "typenum" 1395 - version = "1.15.0" 1954 + version = "1.18.0" 1396 1955 source = "registry+https://github.com/rust-lang/crates.io-index" 1397 - checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1956 + checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1398 1957 1399 1958 [[package]] 1400 1959 name = "unicase" 1401 - version = "2.6.0" 1960 + version = "2.8.1" 1402 1961 source = "registry+https://github.com/rust-lang/crates.io-index" 1403 - checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1404 - dependencies = [ 1405 - "version_check", 1406 - ] 1962 + checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 1407 1963 1408 1964 [[package]] 1409 1965 name = "unicode-bidi" 1410 - version = "0.3.8" 1966 + version = "0.3.18" 1411 1967 source = "registry+https://github.com/rust-lang/crates.io-index" 1412 - checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1968 + checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 1413 1969 1414 1970 [[package]] 1415 1971 name = "unicode-ident" 1416 - version = "1.0.5" 1972 + version = "1.0.18" 1417 1973 source = "registry+https://github.com/rust-lang/crates.io-index" 1418 - checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1974 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1419 1975 1420 1976 [[package]] 1421 1977 name = "unicode-normalization" 1422 - version = "0.1.22" 1978 + version = "0.1.24" 1423 1979 source = "registry+https://github.com/rust-lang/crates.io-index" 1424 - checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1980 + checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 1425 1981 dependencies = [ 1426 1982 "tinyvec", 1427 1983 ] 1428 1984 1429 1985 [[package]] 1986 + name = "unicode-properties" 1987 + version = "0.1.3" 1988 + source = "registry+https://github.com/rust-lang/crates.io-index" 1989 + checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 1990 + 1991 + [[package]] 1430 1992 name = "url" 1431 - version = "2.3.1" 1993 + version = "2.5.4" 1432 1994 source = "registry+https://github.com/rust-lang/crates.io-index" 1433 - checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1995 + checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1434 1996 dependencies = [ 1435 1997 "form_urlencoded", 1436 1998 "idna", ··· 1444 2006 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1445 2007 1446 2008 [[package]] 2009 + name = "utf8_iter" 2010 + version = "1.0.4" 2011 + source = "registry+https://github.com/rust-lang/crates.io-index" 2012 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2013 + 2014 + [[package]] 2015 + name = "utf8parse" 2016 + version = "0.2.2" 2017 + source = "registry+https://github.com/rust-lang/crates.io-index" 2018 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2019 + 2020 + [[package]] 1447 2021 name = "vcpkg" 1448 2022 version = "0.2.15" 1449 2023 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1451 2025 1452 2026 [[package]] 1453 2027 name = "version_check" 1454 - version = "0.9.4" 2028 + version = "0.9.5" 1455 2029 source = "registry+https://github.com/rust-lang/crates.io-index" 1456 - checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2030 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1457 2031 1458 2032 [[package]] 1459 2033 name = "want" 1460 - version = "0.3.0" 2034 + version = "0.3.1" 1461 2035 source = "registry+https://github.com/rust-lang/crates.io-index" 1462 - checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 2036 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1463 2037 dependencies = [ 1464 - "log", 1465 2038 "try-lock", 1466 2039 ] 1467 2040 1468 2041 [[package]] 1469 2042 name = "warp" 1470 - version = "0.3.3" 2043 + version = "0.3.7" 1471 2044 source = "registry+https://github.com/rust-lang/crates.io-index" 1472 - checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" 2045 + checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" 1473 2046 dependencies = [ 1474 2047 "bytes", 1475 2048 "futures-channel", 1476 2049 "futures-util", 1477 2050 "headers", 1478 - "http", 2051 + "http 0.2.12", 1479 2052 "hyper", 1480 2053 "log", 1481 2054 "mime", 1482 2055 "mime_guess", 1483 - "multipart", 2056 + "multer", 1484 2057 "percent-encoding", 1485 2058 "pin-project", 1486 - "rustls-pemfile", 1487 2059 "scoped-tls", 1488 2060 "serde", 1489 2061 "serde_json", 1490 2062 "serde_urlencoded", 1491 2063 "tokio", 1492 - "tokio-stream", 1493 2064 "tokio-tungstenite", 1494 2065 "tokio-util", 1495 2066 "tower-service", ··· 1503 2074 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1504 2075 1505 2076 [[package]] 1506 - name = "winapi" 1507 - version = "0.3.9" 2077 + name = "wasi" 2078 + version = "0.14.2+wasi-0.2.4" 2079 + source = "registry+https://github.com/rust-lang/crates.io-index" 2080 + checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 2081 + dependencies = [ 2082 + "wit-bindgen-rt", 2083 + ] 2084 + 2085 + [[package]] 2086 + name = "wasite" 2087 + version = "0.1.0" 2088 + source = "registry+https://github.com/rust-lang/crates.io-index" 2089 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 2090 + 2091 + [[package]] 2092 + name = "wasm-bindgen" 2093 + version = "0.2.100" 2094 + source = "registry+https://github.com/rust-lang/crates.io-index" 2095 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 2096 + dependencies = [ 2097 + "cfg-if", 2098 + "once_cell", 2099 + "wasm-bindgen-macro", 2100 + ] 2101 + 2102 + [[package]] 2103 + name = "wasm-bindgen-backend" 2104 + version = "0.2.100" 2105 + source = "registry+https://github.com/rust-lang/crates.io-index" 2106 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 2107 + dependencies = [ 2108 + "bumpalo", 2109 + "log", 2110 + "proc-macro2", 2111 + "quote", 2112 + "syn", 2113 + "wasm-bindgen-shared", 2114 + ] 2115 + 2116 + [[package]] 2117 + name = "wasm-bindgen-macro" 2118 + version = "0.2.100" 2119 + source = "registry+https://github.com/rust-lang/crates.io-index" 2120 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 2121 + dependencies = [ 2122 + "quote", 2123 + "wasm-bindgen-macro-support", 2124 + ] 2125 + 2126 + [[package]] 2127 + name = "wasm-bindgen-macro-support" 2128 + version = "0.2.100" 2129 + source = "registry+https://github.com/rust-lang/crates.io-index" 2130 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 2131 + dependencies = [ 2132 + "proc-macro2", 2133 + "quote", 2134 + "syn", 2135 + "wasm-bindgen-backend", 2136 + "wasm-bindgen-shared", 2137 + ] 2138 + 2139 + [[package]] 2140 + name = "wasm-bindgen-shared" 2141 + version = "0.2.100" 2142 + source = "registry+https://github.com/rust-lang/crates.io-index" 2143 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 2144 + dependencies = [ 2145 + "unicode-ident", 2146 + ] 2147 + 2148 + [[package]] 2149 + name = "web-sys" 2150 + version = "0.3.77" 1508 2151 source = "registry+https://github.com/rust-lang/crates.io-index" 1509 - checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2152 + checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1510 2153 dependencies = [ 1511 - "winapi-i686-pc-windows-gnu", 1512 - "winapi-x86_64-pc-windows-gnu", 2154 + "js-sys", 2155 + "wasm-bindgen", 1513 2156 ] 1514 2157 1515 2158 [[package]] 1516 - name = "winapi-i686-pc-windows-gnu" 1517 - version = "0.4.0" 2159 + name = "whoami" 2160 + version = "1.6.0" 1518 2161 source = "registry+https://github.com/rust-lang/crates.io-index" 1519 - checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2162 + checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" 2163 + dependencies = [ 2164 + "redox_syscall", 2165 + "wasite", 2166 + "web-sys", 2167 + ] 1520 2168 1521 2169 [[package]] 1522 2170 name = "winapi-util" 1523 - version = "0.1.5" 2171 + version = "0.1.9" 1524 2172 source = "registry+https://github.com/rust-lang/crates.io-index" 1525 - checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2173 + checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1526 2174 dependencies = [ 1527 - "winapi", 2175 + "windows-sys 0.59.0", 1528 2176 ] 1529 2177 1530 2178 [[package]] 1531 - name = "winapi-x86_64-pc-windows-gnu" 1532 - version = "0.4.0" 2179 + name = "windows-sys" 2180 + version = "0.52.0" 1533 2181 source = "registry+https://github.com/rust-lang/crates.io-index" 1534 - checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2182 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2183 + dependencies = [ 2184 + "windows-targets", 2185 + ] 1535 2186 1536 2187 [[package]] 1537 2188 name = "windows-sys" 1538 - version = "0.36.1" 2189 + version = "0.59.0" 2190 + source = "registry+https://github.com/rust-lang/crates.io-index" 2191 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2192 + dependencies = [ 2193 + "windows-targets", 2194 + ] 2195 + 2196 + [[package]] 2197 + name = "windows-targets" 2198 + version = "0.52.6" 1539 2199 source = "registry+https://github.com/rust-lang/crates.io-index" 1540 - checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 2200 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1541 2201 dependencies = [ 2202 + "windows_aarch64_gnullvm", 1542 2203 "windows_aarch64_msvc", 1543 2204 "windows_i686_gnu", 2205 + "windows_i686_gnullvm", 1544 2206 "windows_i686_msvc", 1545 2207 "windows_x86_64_gnu", 2208 + "windows_x86_64_gnullvm", 1546 2209 "windows_x86_64_msvc", 1547 2210 ] 1548 2211 1549 2212 [[package]] 2213 + name = "windows_aarch64_gnullvm" 2214 + version = "0.52.6" 2215 + source = "registry+https://github.com/rust-lang/crates.io-index" 2216 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2217 + 2218 + [[package]] 1550 2219 name = "windows_aarch64_msvc" 1551 - version = "0.36.1" 2220 + version = "0.52.6" 1552 2221 source = "registry+https://github.com/rust-lang/crates.io-index" 1553 - checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 2222 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1554 2223 1555 2224 [[package]] 1556 2225 name = "windows_i686_gnu" 1557 - version = "0.36.1" 2226 + version = "0.52.6" 2227 + source = "registry+https://github.com/rust-lang/crates.io-index" 2228 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2229 + 2230 + [[package]] 2231 + name = "windows_i686_gnullvm" 2232 + version = "0.52.6" 1558 2233 source = "registry+https://github.com/rust-lang/crates.io-index" 1559 - checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 2234 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1560 2235 1561 2236 [[package]] 1562 2237 name = "windows_i686_msvc" 1563 - version = "0.36.1" 2238 + version = "0.52.6" 1564 2239 source = "registry+https://github.com/rust-lang/crates.io-index" 1565 - checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 2240 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1566 2241 1567 2242 [[package]] 1568 2243 name = "windows_x86_64_gnu" 1569 - version = "0.36.1" 2244 + version = "0.52.6" 2245 + source = "registry+https://github.com/rust-lang/crates.io-index" 2246 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2247 + 2248 + [[package]] 2249 + name = "windows_x86_64_gnullvm" 2250 + version = "0.52.6" 1570 2251 source = "registry+https://github.com/rust-lang/crates.io-index" 1571 - checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 2252 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1572 2253 1573 2254 [[package]] 1574 2255 name = "windows_x86_64_msvc" 1575 - version = "0.36.1" 2256 + version = "0.52.6" 1576 2257 source = "registry+https://github.com/rust-lang/crates.io-index" 1577 - checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 2258 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2259 + 2260 + [[package]] 2261 + name = "winnow" 2262 + version = "0.7.10" 2263 + source = "registry+https://github.com/rust-lang/crates.io-index" 2264 + checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" 2265 + dependencies = [ 2266 + "memchr", 2267 + ] 2268 + 2269 + [[package]] 2270 + name = "wit-bindgen-rt" 2271 + version = "0.39.0" 2272 + source = "registry+https://github.com/rust-lang/crates.io-index" 2273 + checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2274 + dependencies = [ 2275 + "bitflags", 2276 + ] 2277 + 2278 + [[package]] 2279 + name = "writeable" 2280 + version = "0.6.1" 2281 + source = "registry+https://github.com/rust-lang/crates.io-index" 2282 + checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2283 + 2284 + [[package]] 2285 + name = "yoke" 2286 + version = "0.8.0" 2287 + source = "registry+https://github.com/rust-lang/crates.io-index" 2288 + checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2289 + dependencies = [ 2290 + "serde", 2291 + "stable_deref_trait", 2292 + "yoke-derive", 2293 + "zerofrom", 2294 + ] 2295 + 2296 + [[package]] 2297 + name = "yoke-derive" 2298 + version = "0.8.0" 2299 + source = "registry+https://github.com/rust-lang/crates.io-index" 2300 + checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2301 + dependencies = [ 2302 + "proc-macro2", 2303 + "quote", 2304 + "syn", 2305 + "synstructure", 2306 + ] 2307 + 2308 + [[package]] 2309 + name = "zerocopy" 2310 + version = "0.8.25" 2311 + source = "registry+https://github.com/rust-lang/crates.io-index" 2312 + checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 2313 + dependencies = [ 2314 + "zerocopy-derive", 2315 + ] 2316 + 2317 + [[package]] 2318 + name = "zerocopy-derive" 2319 + version = "0.8.25" 2320 + source = "registry+https://github.com/rust-lang/crates.io-index" 2321 + checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 2322 + dependencies = [ 2323 + "proc-macro2", 2324 + "quote", 2325 + "syn", 2326 + ] 2327 + 2328 + [[package]] 2329 + name = "zerofrom" 2330 + version = "0.1.6" 2331 + source = "registry+https://github.com/rust-lang/crates.io-index" 2332 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2333 + dependencies = [ 2334 + "zerofrom-derive", 2335 + ] 2336 + 2337 + [[package]] 2338 + name = "zerofrom-derive" 2339 + version = "0.1.6" 2340 + source = "registry+https://github.com/rust-lang/crates.io-index" 2341 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2342 + dependencies = [ 2343 + "proc-macro2", 2344 + "quote", 2345 + "syn", 2346 + "synstructure", 2347 + ] 2348 + 2349 + [[package]] 2350 + name = "zerotrie" 2351 + version = "0.2.2" 2352 + source = "registry+https://github.com/rust-lang/crates.io-index" 2353 + checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2354 + dependencies = [ 2355 + "displaydoc", 2356 + "yoke", 2357 + "zerofrom", 2358 + ] 2359 + 2360 + [[package]] 2361 + name = "zerovec" 2362 + version = "0.11.2" 2363 + source = "registry+https://github.com/rust-lang/crates.io-index" 2364 + checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" 2365 + dependencies = [ 2366 + "yoke", 2367 + "zerofrom", 2368 + "zerovec-derive", 2369 + ] 2370 + 2371 + [[package]] 2372 + name = "zerovec-derive" 2373 + version = "0.11.1" 2374 + source = "registry+https://github.com/rust-lang/crates.io-index" 2375 + checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2376 + dependencies = [ 2377 + "proc-macro2", 2378 + "quote", 2379 + "syn", 2380 + ]
+1
Cargo.toml
··· 1 1 [workspace] 2 + resolver = "2" 2 3 members = [ 3 4 "common", 4 5 "lumen",
+21 -13
Dockerfile
··· 1 - FROM debian:buster-slim 1 + FROM rust:1.87.0-slim-bookworm 2 2 ARG DEBIAN_FRONTEND=noninteractive 3 - ENV RUSTUP_HOME=/usr/local/rustup \ 4 - CARGO_HOME=/usr/local/cargo \ 5 - PATH=/usr/local/cargo/bin:$PATH 6 - RUN apt-get update && apt-get install -y --no-install-recommends --no-install-suggests ca-certificates pkg-config libssl-dev gcc-multilib curl && \ 7 - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s \ 8 - -- --profile minimal -y 3 + RUN apt-get update && apt-get install -y --no-install-recommends --no-install-suggests ca-certificates pkg-config libssl-dev libpq-dev 4 + ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse 5 + 6 + RUN --mount=type=cache,target=$CARGO_HOME/registry \ 7 + cargo install diesel_cli --version 2.2.10 --no-default-features --features postgres 8 + 9 9 COPY common /lumen/common 10 10 COPY lumen /lumen/lumen 11 11 COPY Cargo.toml /lumen/ 12 - RUN cd /lumen && cargo build --release 12 + RUN --mount=type=cache,target=$CARGO_HOME/registry,target=/lumen/target \ 13 + cd /lumen && cargo build --release && cp /lumen/target/release/lumen /root/ 13 14 14 - FROM debian:buster-slim 15 + FROM debian:bookworm-slim 15 16 ARG DEBIAN_FRONTEND=noninteractive 16 - RUN apt-get update && apt-get install -y --no-install-recommends --no-install-suggests openssl netcat-openbsd && \ 17 - sed -i -e 's,\[ v3_req \],\[ v3_req \]\nextendedKeyUsage = serverAuth,' /etc/ssl/openssl.cnf 18 - COPY --from=0 /lumen/target/release/lumen /usr/bin/lumen 17 + RUN apt-get update && apt-get install -y --no-install-recommends --no-install-suggests openssl libpq5 && \ 18 + sed -i -e 's,\[ v3_req \],\[ v3_req \]\nextendedKeyUsage = serverAuth,' /etc/ssl/openssl.cnf 19 + RUN mkdir /usr/lib/lumen/ 20 + 21 + COPY --from=0 /usr/local/cargo/bin/diesel /usr/bin/diesel 22 + COPY --from=0 /lumen/common/migrations /usr/lib/lumen/migrations 23 + COPY --from=0 /lumen/common/diesel.toml /usr/lib/lumen/ 24 + COPY --from=0 /root/lumen /usr/bin/lumen 25 + 19 26 COPY config-example.toml docker-init.sh /lumen/ 20 - RUN chmod ug+x /lumen/docker-init.sh && chmod ug+x /usr/bin/lumen 27 + RUN chmod a+x /lumen/docker-init.sh && chmod a+x /usr/bin/lumen 21 28 WORKDIR /lumen 29 + CMD /lumen/docker-init.sh
+125 -76
README.md
··· 1 - # Lumen 2 - A private Lumina server that can be used with IDA Pro 7.2+. 3 - 4 - [lumen.abda.nl](https://lumen.abda.nl/) runs this server. 5 - 6 - You can read about the protocol research [here](https://abda.nl/posts/introducing-lumen/). 7 - 8 - ## Features 9 - - Stores function signatures so you (and your team) can quickly identify functions that you found in the past using IDA's built-in Lumina features. 10 - - Backed by PostgreSQL 11 - - Experimental HTTP API that allows querying the database for comments by file or function hash. 12 - 13 - ## Getting Started 14 - ### Binary releases 15 - Release binaries are available at https://github.com/naim94a/lumen/releases/latest. 16 - 17 - ### Building from source with Rust 18 - 1. `git clone https://github.com/naim94a/lumen.git` 19 - 2. Get a rust toolchain: https://rustup.rs/ 20 - 3. `cd lumen` 21 - 4. Setup a Postgres database and execute src/schema.sql on it 22 - 5. `cargo build --release` 23 - 24 - ### Docker Method 25 - 1. Install `docker-engine` and `docker-compose`. 26 - 2. If using a custom TLS certificate, copy the private key (`.p12`/`.pfx` extension) to `./dockershare` and set the key password in `.env` as `PKCSPASSWD`. 27 - 3. If using a custom Lumen config, copy it to `./dockershare/config.toml`. 28 - 4. Otherwise, or if you have finished these steps, just run `docker-compose up`. 29 - 5. Regardless, if TLS is enabled in the `config.toml`, a `hexrays.crt` will be generated in `./dockershare` to be copied to the IDA install directory. 30 - 31 - ### Usage 32 - ``` 33 - ./lumen -c config.toml 34 - ``` 35 - 36 - ### Configuring IDA 37 - You will need IDA Pro 7.2 or above in order to use _lumen_. 38 - 39 - > The following information may get sent to _lumen_ server: IDA key, Hostname, IDB path, original file path, file MD5, function signature, stack frames & comments. 40 - 41 - - In your IDA's installation directory open "cfg\ida.cfg" with your favorite text editor _(Example: C:\Program Files\IDA Pro 7.5\cfg\ida.cfg)_ 42 - - Locate the commented out `LUMINA_HOST`, `LUMINA_PORT`, and change their values to the address of your _lumen_ server. 43 - - If you didn't configure TLS, Add "LUMINA_TLS = NO" after the line with `LUMINA_PORT`. 44 - 45 - Example: 46 - ```C 47 - LUMINA_HOST = "192.168.1.1"; 48 - LUMINA_PORT = 1234 49 - 50 - // Only if TLS isn't used: 51 - LUMINA_TLS = NO 52 - ``` 53 - 54 - ### Configuring TLS 55 - IDA Pro uses a pinned certificate for Lumina's communcation, so adding a self-signed certificate to your root certificates won't work. 56 - Luckily, we can override the hard-coded public key by writing a DER-base64 encoded certificate to "hexrays.crt" in IDA's install directory. 57 - 58 - You may find the following commands useful: 59 - ```bash 60 - # create a certificate 61 - openssl req -x509 -newkey rsa:4096 -keyout lumen_key.pem -out lumen_crt.pem -days 365 -nodes 62 - 63 - # convert to pkcs12 for lumen; used for `lumen.tls` in config 64 - openssl pkcs12 -export -out lumen.p12 -inkey lumen_key.pem -in lumen_crt.pem 65 - 66 - # export public-key for IDA; Copy hexrays.crt to IDA installation folder 67 - openssl x509 -in lumen_crt.pem -out hexrays.crt 68 - ``` 69 - 70 - No attempt is made to merge function data - this may casuse a situation where metadata is inconsistent. 71 - Instead, the metadata with the highest calculated score is returned to the user. 72 - 73 - 74 - --- 75 - 76 - Developed by [Naim A.](https://github.com/naim94a); License: MIT. 1 + # Lumen 2 + 3 + A private Lumina server that can be used with IDA Pro 7.2+. 4 + 5 + [lumen.abda.nl](https://lumen.abda.nl/) runs this server. 6 + 7 + You can read about the protocol research [here](https://abda.nl/posts/introducing-lumen/). 8 + 9 + ## Features 10 + 11 + - Stores function signatures so you (and your team) can quickly identify functions that you found in the past using IDA's built-in Lumina features. 12 + - Backed by PostgreSQL 13 + - Experimental HTTP API that allows querying the database for comments by file or function hash. 14 + 15 + ## Getting Started 16 + 17 + ### Docker Method (Recommended) 18 + 19 + In this method precompiled docker images will be downloaded, All you need is [docker-compose.yml](./docker-compose.yml). 20 + 21 + 1. Install `docker-engine` and `docker-compose`. 22 + 2. If using a custom TLS certificate, copy the private key (`.p12`/`.pfx` extension) to `./dockershare` and set the key password in `.env` as `PKCSPASSWD`. 23 + 3. If using a custom Lumen config, copy it to `./dockershare/config.toml`. 24 + 4. Otherwise, or if you have finished these steps, just run `docker-compose up`. 25 + 5. Regardless, if TLS is enabled in the `config.toml`, a `hexrays.crt` will be generated in `./dockershare` to be copied to the IDA install directory. 26 + 27 + ### Building from source with Rust 28 + 29 + 1. `git clone https://github.com/naim94a/lumen.git` 30 + 2. Get a rust toolchain: https://rustup.rs/ 31 + 3. `cd lumen` 32 + 4. Setup a the database 33 + 34 + - install postgres 35 + - install diesel-cli and run migrations: 36 + 37 + ```bash 38 + cargo install diesel_cli --no-default-features -Fpostgres 39 + diesel --config-file common/diesel.toml \ 40 + --database-url postgres://postgres:password@localhost/lumen \ 41 + migration run 42 + ``` 43 + 44 + 5. `cargo build --release` 45 + 46 + ### Usage 47 + 48 + ```bash 49 + ./lumen -c config.toml 50 + ``` 51 + 52 + ### Configuring IDA 53 + 54 + #### IDA Pro >= 8.1 55 + 56 + If you used LUMEN in the past, remove the LUMINA settings in the ida.cfg or idauser.cfg files, otherwise you will get a warning about 57 + bad config parameters. 58 + 59 + ##### Setup under Linux : 60 + 61 + ```bash 62 + #!/bin/sh 63 + export LUMINA_TLS=false 64 + $1 65 + ``` 66 + 67 + - save as ida_lumen.sh, "chmod +x ida_lumen.sh", now you can run IDA using "./ida_lumen.sh ./ida" or "./ida_lumen ./ida64" 68 + 69 + ##### Setup under Windows : 70 + 71 + ```batch 72 + set LUMINA_TLS=false 73 + %1 74 + ``` 75 + 76 + - save as ida_lumen.bat, now you can run IDA using "./ida_lumen.bat ida.exe" or "./ida_lumen.bat ida64.exe" 77 + 78 + ##### Setup IDA 79 + 80 + - Go to Options, General, Lumina. Select "Use a private server", then set your host and port and "guest" as username and password. Click on ok. 81 + 82 + #### IDA Pro < 8.1 83 + 84 + You will need IDA Pro 7.2 or above in order to use _lumen_. 85 + 86 + > The following information may get sent to _lumen_ server: IDA key, Hostname, IDB path, original file path, file MD5, function signature, stack frames & comments. 87 + 88 + - In your IDA's installation directory open "cfg\ida.cfg" with your favorite text editor _(Example: C:\Program Files\IDA Pro 7.5\cfg\ida.cfg)_ 89 + - Locate the commented out `LUMINA_HOST`, `LUMINA_PORT`, and change their values to the address of your _lumen_ server. 90 + - If you didn't configure TLS, Add "LUMINA_TLS = NO" after the line with `LUMINA_PORT`. 91 + 92 + Example: 93 + 94 + ```C 95 + LUMINA_HOST = "192.168.1.1"; 96 + LUMINA_PORT = 1234 97 + 98 + // Only if TLS isn't used: 99 + LUMINA_TLS = NO 100 + ``` 101 + 102 + ### Configuring TLS 103 + 104 + IDA Pro uses a pinned certificate for Lumina's communcation, so adding a self-signed certificate to your root certificates won't work. 105 + Luckily, we can override the hard-coded public key by writing a DER-base64 encoded certificate to "hexrays.crt" in IDA's install directory. 106 + 107 + You may find the following commands useful: 108 + 109 + ```bash 110 + # create a certificate 111 + openssl req -x509 -newkey rsa:4096 -keyout lumen_key.pem -out lumen_crt.pem -days 365 -nodes 112 + 113 + # convert to pkcs12 for lumen; used for `lumen.tls` in config 114 + openssl pkcs12 -export -out lumen.p12 -inkey lumen_key.pem -in lumen_crt.pem 115 + 116 + # export public-key for IDA; Copy hexrays.crt to IDA installation folder 117 + openssl x509 -in lumen_crt.pem -out hexrays.crt 118 + ``` 119 + 120 + No attempt is made to merge function data - this may cause a situation where metadata is inconsistent. 121 + Instead, the metadata with the highest calculated score is returned to the user. 122 + 123 + --- 124 + 125 + Developed by [Naim A.](https://github.com/naim94a); License: MIT.
+30 -10
common/Cargo.toml
··· 3 3 description = "common utilities for lumen" 4 4 version = "0.2.0" 5 5 authors = ["Naim A. <naim@abda.nl>"] 6 - edition = "2018" 6 + edition = "2021" 7 7 publish = false 8 8 9 9 [dependencies] 10 - tokio = {version = "1.21", features = ["full"], optional = true} 11 - log = {version = "0.4", features = ["release_max_level_debug"]} 12 - serde = {version = "1.0", features = ["derive"]} 13 - tokio-postgres = {version = "0.7", optional = true} 14 - postgres-native-tls = {version = "0.5", optional = true} 15 - native-tls = {version = "0.2", optional = true} 10 + tokio = { version = "1.39", features = ["full"], optional = true } 11 + log = { version = "0.4", features = ["release_max_level_debug"] } 12 + serde = { version = "1.0", features = ["derive"] } 13 + postgres-native-tls = { version = "0.5", optional = true } 14 + native-tls = { version = "0.2", optional = true } 16 15 futures-util = "0.3" 17 - toml = "0.5" 18 - warp = {version = "0.3", optional = true} 16 + toml = "0.8" 17 + warp = { version = "0.3", optional = true } 19 18 binascii = "0.1" 20 19 20 + tokio-postgres = { version = "0.7", default-features = false, optional = true } 21 + diesel = { version = "2.2", optional = true, default-features = false, features = [ 22 + "postgres_backend", 23 + "time", 24 + ] } 25 + time = { version = "0.3.36", optional = true } 26 + diesel-async = { version = "0.5", optional = true, features = [ 27 + "postgres", 28 + "bb8", 29 + ] } 30 + anyhow = "1.0" 31 + prometheus-client = "0.22" 32 + 21 33 [features] 22 34 default = ["web", "db"] 23 35 web = ["warp"] 24 - db = ["tokio", "tokio-postgres", "postgres-native-tls", "native-tls"] 36 + db = [ 37 + "tokio", 38 + "postgres-native-tls", 39 + "native-tls", 40 + "diesel", 41 + "diesel-async", 42 + "tokio-postgres", 43 + "time", 44 + ]
+8
common/diesel.toml
··· 1 + # For documentation on how to configure this file, 2 + # see https://diesel.rs/guides/configuring-diesel-cli 3 + 4 + [print_schema] 5 + file = "src/db/schema_auto.rs" 6 + 7 + [migrations_directory] 8 + dir = "migrations"
common/migrations/.keep

This is a binary file and will not be displayed.

+6
common/migrations/00000000000000_diesel_initial_setup/down.sql
··· 1 + -- This file was automatically created by Diesel to setup helper functions 2 + -- and other internal bookkeeping. This file is safe to edit, any future 3 + -- changes will be added to existing projects as new migrations. 4 + 5 + DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); 6 + DROP FUNCTION IF EXISTS diesel_set_updated_at();
+36
common/migrations/00000000000000_diesel_initial_setup/up.sql
··· 1 + -- This file was automatically created by Diesel to setup helper functions 2 + -- and other internal bookkeeping. This file is safe to edit, any future 3 + -- changes will be added to existing projects as new migrations. 4 + 5 + 6 + 7 + 8 + -- Sets up a trigger for the given table to automatically set a column called 9 + -- `updated_at` whenever the row is modified (unless `updated_at` was included 10 + -- in the modified columns) 11 + -- 12 + -- # Example 13 + -- 14 + -- ```sql 15 + -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); 16 + -- 17 + -- SELECT diesel_manage_updated_at('users'); 18 + -- ``` 19 + CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ 20 + BEGIN 21 + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s 22 + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); 23 + END; 24 + $$ LANGUAGE plpgsql; 25 + 26 + CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ 27 + BEGIN 28 + IF ( 29 + NEW IS DISTINCT FROM OLD AND 30 + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at 31 + ) THEN 32 + NEW.updated_at := current_timestamp; 33 + END IF; 34 + RETURN NEW; 35 + END; 36 + $$ LANGUAGE plpgsql;
+39
common/migrations/2023-02-01-210714_init/up.sql
··· 1 + CREATE TABLE IF NOT EXISTS users ( 2 + id SERIAL PRIMARY KEY, 3 + lic_id bytea, 4 + lic_data bytea, 5 + hostname VARCHAR(260), 6 + first_seen TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP 7 + ); 8 + CREATE UNIQUE INDEX IF NOT EXISTS user_rec ON users(lic_id,lic_data,hostname); 9 + CREATE UNIQUE INDEX IF NOT EXISTS user_hn_null ON users (lic_id,lic_data, (hostname IS NULL)) WHERE hostname is NULL; 10 + 11 + CREATE TABLE IF NOT EXISTS files ( 12 + id SERIAL PRIMARY KEY, 13 + chksum bytea UNIQUE /* file chksum */ 14 + ); 15 + 16 + CREATE TABLE IF NOT EXISTS dbs ( 17 + id SERIAL PRIMARY KEY, 18 + file_path VARCHAR(260), 19 + idb_path VARCHAR(260), 20 + file_id INTEGER REFERENCES files(id), 21 + user_id INTEGER REFERENCES users(id) 22 + ); 23 + CREATE UNIQUE INDEX IF NOT EXISTS db_paths ON dbs(file_id, user_id, idb_path); 24 + 25 + CREATE TABLE IF NOT EXISTS funcs ( 26 + id SERIAL PRIMARY KEY, 27 + name TEXT NOT NULL, 28 + len INTEGER NOT NULL, 29 + db_id INTEGER REFERENCES dbs(id) NOT NULL, 30 + chksum bytea, /* function chksum */ 31 + metadata bytea, 32 + rank INTEGER, 33 + 34 + push_dt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 35 + update_dt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP 36 + ); 37 + CREATE UNIQUE INDEX IF NOT EXISTS funcs_db ON funcs(chksum, db_id); 38 + CREATE INDEX IF NOT EXISTS funcs_ranking ON funcs(chksum,rank); 39 + CREATE INDEX IF NOT EXISTS func_chksum ON funcs(chksum);
+77
common/src/async_drop.rs
··· 1 + use futures_util::{future::BoxFuture, Future}; 2 + use log::trace; 3 + use tokio::sync::mpsc::{unbounded_channel, UnboundedSender, WeakUnboundedSender}; 4 + 5 + enum AsyncDropperMsg { 6 + Future(BoxFuture<'static, ()>), 7 + Termination, 8 + } 9 + 10 + /// Assists with executing code in a future when the future is cancelled. 11 + pub struct AsyncDropper { 12 + tx: UnboundedSender<AsyncDropperMsg>, 13 + } 14 + impl AsyncDropper { 15 + /// Creates a new `AsyncDropper` 16 + /// Returns a tuple containing the AsyncDropper struct and a future that will perform tasks when a future is dropped. 17 + #[track_caller] 18 + pub fn new() -> (AsyncDropper, impl Future<Output = ()> + 'static) { 19 + let orig = format!("{}", std::panic::Location::caller()); 20 + trace!("new dropper '{orig}'"); 21 + 22 + let (tx, mut rx) = unbounded_channel(); 23 + let fut = async move { 24 + while let Some(msg) = rx.recv().await { 25 + match msg { 26 + AsyncDropperMsg::Future(fut) => { 27 + fut.await; 28 + }, 29 + AsyncDropperMsg::Termination => { 30 + trace!("term received for '{orig}'..."); 31 + break; 32 + }, 33 + } 34 + } 35 + trace!("dropper '{orig}' exited."); 36 + }; 37 + 38 + (Self { tx }, fut) 39 + } 40 + 41 + /// Defers execution of a future to when the returned `AsyncDropGuard` is dropped 42 + pub fn defer<F: Future<Output = ()> + Send + 'static>(&self, fut: F) -> AsyncDropGuard { 43 + let tx = self.tx.downgrade(); 44 + AsyncDropGuard { tx, run: Some(Box::pin(fut)) } 45 + } 46 + } 47 + impl Drop for AsyncDropper { 48 + fn drop(&mut self) { 49 + if let Err(err) = self.tx.send(AsyncDropperMsg::Termination) { 50 + // If this ever panics, we're not cleaning up resources properly. 51 + panic!("failed to send termination: {}", err); 52 + } 53 + } 54 + } 55 + 56 + /// A Guard struct. When dropped executes the defered future. 57 + pub struct AsyncDropGuard { 58 + tx: WeakUnboundedSender<AsyncDropperMsg>, 59 + run: Option<BoxFuture<'static, ()>>, 60 + } 61 + 62 + impl AsyncDropGuard { 63 + /// This consumes the guard, causing the internal future not to execute. 64 + pub fn consume(mut self) { 65 + self.run.take(); 66 + } 67 + } 68 + 69 + impl Drop for AsyncDropGuard { 70 + fn drop(&mut self) { 71 + if let Some(fun) = self.run.take() { 72 + if let Some(tx) = self.tx.upgrade() { 73 + let _ = tx.send(AsyncDropperMsg::Future(fun)); 74 + } 75 + } 76 + } 77 + }
+92 -54
common/src/config.rs
··· 1 - use serde::Deserialize; 2 - use toml::from_slice; 3 - use std::{net::SocketAddr, path::PathBuf}; 4 - 5 - #[derive(Deserialize)] 6 - pub struct TlsIdentity { 7 - pub server_cert: PathBuf, 8 - } 9 - 10 - #[derive(Deserialize)] 11 - pub struct LuminaServer { 12 - pub bind_addr: SocketAddr, 13 - pub use_tls: Option<bool>, 14 - pub tls: Option<TlsIdentity>, 15 - pub server_name: Option<String>, 16 - } 17 - 18 - #[derive(Deserialize)] 19 - pub struct WebServer { 20 - pub bind_addr: SocketAddr, 21 - } 22 - 23 - #[derive(Deserialize)] 24 - pub struct Database { 25 - pub connection_info: String, 26 - 27 - pub use_tls: bool, 28 - pub server_ca: Option<PathBuf>, 29 - pub client_id: Option<PathBuf>, 30 - } 31 - 32 - #[derive(Deserialize)] 33 - pub struct Config { 34 - pub lumina: LuminaServer, 35 - pub api_server: Option<WebServer>, 36 - pub database: Database, 37 - } 38 - 39 - pub trait HasConfig { 40 - fn get_config(&self) -> &Config; 41 - } 42 - 43 - impl HasConfig for Config { 44 - fn get_config(&self) -> &Config { 45 - self 46 - } 47 - } 48 - 49 - pub fn load_config<R: std::io::Read>(mut fd: R) -> Config { 50 - let mut buf = vec![]; 51 - fd.read_to_end(&mut buf).expect("failed to read config"); 52 - 53 - from_slice(&buf).expect("failed to parse configuration") 54 - } 1 + use serde::Deserialize; 2 + use std::time::Duration; 3 + use std::{net::SocketAddr, path::PathBuf}; 4 + use toml::from_str; 5 + 6 + #[derive(Deserialize)] 7 + pub struct TlsIdentity { 8 + pub server_cert: PathBuf, 9 + } 10 + 11 + #[derive(Deserialize)] 12 + pub struct LuminaServer { 13 + pub bind_addr: SocketAddr, 14 + pub use_tls: Option<bool>, 15 + pub tls: Option<TlsIdentity>, 16 + pub server_name: Option<String>, 17 + pub allow_deletes: Option<bool>, 18 + 19 + /// limit of function histories to return per function. 20 + /// `None`, or `Some(0)` will disable the feature on the server. 21 + pub get_history_limit: Option<u32>, 22 + } 23 + 24 + #[derive(Deserialize)] 25 + pub struct WebServer { 26 + pub bind_addr: SocketAddr, 27 + } 28 + 29 + #[derive(Deserialize)] 30 + pub struct Database { 31 + pub connection_info: String, 32 + 33 + pub use_tls: bool, 34 + pub server_ca: Option<PathBuf>, 35 + pub client_id: Option<PathBuf>, 36 + } 37 + 38 + #[derive(Deserialize, Debug)] 39 + #[serde(default)] 40 + pub struct Limits { 41 + /// Maximum time to wait on an idle connection between commands. 42 + pub command_timeout: Duration, 43 + 44 + /// Maximum time to all `PULL_MD` queries. 45 + pub pull_md_timeout: Duration, 46 + 47 + /// Maximum time to wait for `HELO` message. 48 + pub hello_timeout: Duration, 49 + 50 + /// Maximum time allowed until TLS handshake completes. 51 + pub tls_handshake_timeout: Duration, 52 + } 53 + 54 + impl Default for Limits { 55 + fn default() -> Self { 56 + Self { 57 + command_timeout: Duration::from_secs(3600), 58 + pull_md_timeout: Duration::from_secs(4 * 60), 59 + hello_timeout: Duration::from_secs(15), 60 + tls_handshake_timeout: Duration::from_secs(10), 61 + } 62 + } 63 + } 64 + 65 + #[derive(Deserialize)] 66 + pub struct Config { 67 + pub lumina: LuminaServer, 68 + pub api_server: Option<WebServer>, 69 + pub database: Database, 70 + 71 + #[serde(default)] 72 + pub limits: Limits, 73 + } 74 + 75 + pub trait HasConfig { 76 + fn get_config(&self) -> &Config; 77 + } 78 + 79 + impl HasConfig for Config { 80 + fn get_config(&self) -> &Config { 81 + self 82 + } 83 + } 84 + 85 + pub fn load_config<R: std::io::Read>(mut fd: R) -> Config { 86 + let mut buf = vec![]; 87 + fd.read_to_end(&mut buf).expect("failed to read config"); 88 + 89 + let buf = std::str::from_utf8(&buf).expect("file contains invalid utf-8"); 90 + 91 + from_str(buf).expect("failed to parse configuration") 92 + }
+478 -384
common/src/db/mod.rs
··· 1 - use log::*; 2 - use tokio_postgres::{Client, NoTls}; 3 - use serde::Serialize; 4 - use std::{collections::HashMap, sync::Arc}; 5 - use tokio::sync::RwLock; 6 - use crate::config::Config; 7 - 8 - pub type DynConfig = dyn crate::config::HasConfig + Send + Sync; 9 - 10 - pub struct Database { 11 - config: Arc<DynConfig>, 12 - conn: RwLock<Client>, 13 - cache: RwLock<HashMap<String, tokio_postgres::Statement>>, 14 - } 15 - 16 - pub struct FunctionInfo { 17 - pub name: String, 18 - pub len: u32, 19 - pub data: Vec<u8>, 20 - pub popularity: u32, 21 - } 22 - 23 - #[derive(Debug, Serialize)] 24 - pub struct DbStats { 25 - unique_lics: i32, 26 - unique_hosts_per_lic: i32, 27 - 28 - unique_funcs: i32, 29 - total_funcs: i32, 30 - 31 - dbs: i32, 32 - unique_files: i32, 33 - } 34 - 35 - impl Database { 36 - pub async fn open(config: Arc<DynConfig>) -> Result<Self, tokio_postgres::Error> { 37 - let client = Self::connect(config.get_config()).await?; 38 - 39 - Ok(Database{ 40 - config, 41 - conn: RwLock::new(client), 42 - cache: RwLock::new(HashMap::new()), 43 - }) 44 - } 45 - 46 - pub async fn get_conn(&self) -> tokio::sync::RwLockReadGuard<'_, Client> { 47 - self.conn.read().await 48 - } 49 - 50 - async fn connect_tls(conn_info: &Config) -> Result<tokio_postgres::Client, tokio_postgres::Error> { 51 - use postgres_native_tls::MakeTlsConnector; 52 - use native_tls::{TlsConnector, Certificate, Identity}; 53 - 54 - let mut tls_connector = TlsConnector::builder(); 55 - 56 - if let Some(ref client_identity) = conn_info.database.client_id { 57 - let client_identity = tokio::fs::read(client_identity).await.expect("failed to read db's client id"); 58 - let client_identity = Identity::from_pkcs12(&client_identity, "").expect("failed to load db's client identity (PKCS12)"); 59 - tls_connector.identity(client_identity); 60 - } 61 - 62 - if let Some(ref server_ca) = conn_info.database.server_ca { 63 - let server_ca = tokio::fs::read(server_ca).await.expect("failed to read db's server ca"); 64 - let server_ca = Certificate::from_pem(&server_ca).expect("failed to load db's server ca (PEM)"); 65 - tls_connector.add_root_certificate(server_ca); 66 - } 67 - 68 - let tls_connector = tls_connector 69 - .danger_accept_invalid_hostnames(true) 70 - .build() 71 - .expect("failed to build TlsConnector"); 72 - 73 - let connector = MakeTlsConnector::new(tls_connector); 74 - 75 - let (client, conn) = tokio_postgres::connect(&conn_info.database.connection_info, connector).await?; 76 - info!("database connected (tls)."); 77 - 78 - tokio::spawn(async { 79 - if let Err(e) = conn.await { 80 - error!("db connection error: {}", e); 81 - } 82 - }); 83 - 84 - Ok(client) 85 - } 86 - 87 - async fn connect_plain(conn_info: &str) -> Result<tokio_postgres::Client, tokio_postgres::Error> { 88 - let (client, conn) = tokio_postgres::connect(conn_info, NoTls).await?; 89 - info!("database connected."); 90 - 91 - tokio::spawn(async { 92 - if let Err(e) = conn.await { 93 - error!("db connection error: {}", e); 94 - } 95 - }); 96 - 97 - Ok(client) 98 - } 99 - 100 - async fn connect(conn_info: &Config) -> Result<tokio_postgres::Client, tokio_postgres::Error> { 101 - if conn_info.database.use_tls { 102 - info!("connecting with TLS..."); 103 - Self::connect_tls(conn_info).await 104 - } 105 - else { 106 - info!("connecting plain..."); 107 - Self::connect_plain(&conn_info.database.connection_info).await 108 - } 109 - } 110 - 111 - async fn prepare_cached<'a, 'b>(&'a self, sql: &'b str) -> Result<tokio_postgres::Statement, tokio_postgres::Error> { 112 - { 113 - let rd = self.cache.read().await; 114 - if let Some(v) = rd.get(sql) { 115 - return Ok(v.clone()); 116 - } 117 - } 118 - { 119 - let stmt = self.conn.read().await.prepare(sql).await?; 120 - let mut wr = self.cache.write().await; 121 - wr.insert(sql.to_string(), stmt); 122 - 123 - let v = wr.get(sql).expect("failed to get recently added value"); 124 - Ok(v.clone()) 125 - } 126 - } 127 - 128 - pub async fn get_funcs(&self, funcs: &[crate::rpc::PullMetadataFunc<'_>]) -> Result<Vec<Option<FunctionInfo>>, tokio_postgres::Error> { 129 - let stmt = self.prepare_cached(r#" 130 - WITH best AS ( 131 - select chksum,MAX(rank) as maxrank from funcs f1 132 - WHERE chksum = ANY($1) 133 - GROUP BY chksum 134 - ) 135 - SELECT f2.name,f2.len,f2.metadata,f2.chksum FROM best 136 - LEFT JOIN funcs f2 ON (best.chksum=f2.chksum AND best.maxrank=f2.rank) 137 - "#).await?; 138 - 139 - let conn = self.conn.read().await; 140 - 141 - let chksums: Vec<&[u8]> = funcs.iter().map(|v| v.mb_hash).collect(); 142 - 143 - let rows = conn.query(&stmt, &[&chksums]).await?; 144 - let mut partial: HashMap<Vec<u8>, FunctionInfo> = rows 145 - .into_iter() 146 - .map(|row| { 147 - let chksum: Vec<u8> = row.get(3); 148 - let v = FunctionInfo { 149 - name: row.get(0), 150 - len: row.get::<_, i32>(1) as u32, 151 - data: row.get(2), 152 - popularity: 0, 153 - }; 154 - 155 - (chksum, v) 156 - }) 157 - .collect(); 158 - 159 - let results = partial.len(); 160 - 161 - let res: Vec<Option<FunctionInfo>> = chksums.iter().map(|&chksum| { 162 - partial.remove(chksum) 163 - }).collect(); 164 - 165 - trace!("found {}/{} results", results, chksums.len()); 166 - debug_assert_eq!(chksums.len(), res.len()); 167 - Ok(res) 168 - } 169 - 170 - pub async fn get_or_create_user<'a>(&self, user: &'a crate::rpc::RpcHello<'a>, funcs: Option<&'a crate::rpc::PushMetadata<'a>>) -> Result<i32, tokio_postgres::Error> { 171 - let stmt = self.prepare_cached( 172 - r#" 173 - WITH ins AS ( 174 - INSERT INTO users(lic_id, lic_data, hostname) 175 - VALUES ($1, $3, $2) 176 - ON CONFLICT DO NOTHING 177 - RETURNING id 178 - ) 179 - SELECT id FROM ins 180 - UNION 181 - SELECT id FROM users WHERE lic_id=$1 AND lic_data=$3 AND (($2 is not null AND hostname = $2) OR ($2 is null AND hostname is null)) 182 - "#).await?; 183 - 184 - let lic_id = &user.lic_number[..]; 185 - let lic_data = user.license_data; 186 - let hostname = funcs.map(|v| v.hostname); 187 - 188 - let row = self.conn.read().await.query(&stmt, &[&lic_id, &hostname, &lic_data]).await?; 189 - if !row.is_empty() { 190 - let id = row[0].get(0); 191 - if row.len() > 1 { 192 - let vals: Vec<i32> = row.iter().map(|v| v.get(0)).collect(); 193 - debug!("expected single row, got: {:?}", &vals); 194 - } 195 - Ok(id) 196 - } else { 197 - error!("no rows for user. ret 0"); 198 - Ok(0) 199 - } 200 - } 201 - 202 - async fn get_or_create_file<'a>(&self, funcs: &'a crate::rpc::PushMetadata<'a>) -> Result<i32, tokio_postgres::Error> { 203 - let stmt = self.prepare_cached(r#" 204 - WITH ins AS ( 205 - INSERT INTO files(chksum) 206 - VALUES ($1) 207 - ON CONFLICT(chksum) DO NOTHING 208 - RETURNING id 209 - ) 210 - SELECT id FROM files WHERE chksum=$1 211 - UNION 212 - SELECT id FROM ins 213 - "#).await?; 214 - 215 - let hash = &funcs.md5[..]; 216 - 217 - let id: i32 = self.conn.read().await 218 - .query_one(&stmt, &[&hash]).await? 219 - .get(0); 220 - Ok(id) 221 - } 222 - 223 - async fn get_or_create_db<'a>(&self, user: &'a crate::rpc::RpcHello<'a>, funcs: &'a crate::rpc::PushMetadata<'a>) -> Result<i32, tokio_postgres::Error> { 224 - let file_id = self.get_or_create_file(funcs); 225 - let user_id = self.get_or_create_user(user, Some(funcs)); 226 - 227 - let (file_id, user_id): (i32, i32) = futures_util::try_join!(file_id, user_id)?; 228 - 229 - let stmt = self.prepare_cached(r#" 230 - WITH ins AS ( 231 - INSERT INTO dbs (user_id, file_id, file_path, idb_path) 232 - VALUES ($1, $2, $3, $4) 233 - ON CONFLICT(idb_path,file_id,user_id) DO NOTHING 234 - RETURNING id 235 - ) 236 - SELECT id FROM dbs WHERE user_id=$1 AND file_id=$2 AND idb_path=$4 237 - UNION 238 - SELECT id FROM ins 239 - "#).await?; 240 - 241 - let idb_path = funcs.idb_path; 242 - let file_path = funcs.file_path; 243 - 244 - trace!("fid={}; uid={}", file_id, user_id); 245 - let row = self.conn.read().await 246 - .query_one(&stmt, &[&user_id, &file_id, &file_path, &idb_path]).await?; 247 - 248 - let db_id = row.get(0); 249 - 250 - Ok(db_id) 251 - } 252 - 253 - pub async fn push_funcs<'a, 'b>(&'b self, user: &'a crate::rpc::RpcHello<'a>, funcs: &'a crate::rpc::PushMetadata<'a>, scores: &[u32]) -> Result<Vec<bool>, tokio_postgres::Error> { 254 - let db_id = self.get_or_create_db(user, funcs).await?; 255 - 256 - let stmt = self.prepare_cached(r#" 257 - INSERT INTO funcs AS f (name, len, chksum, metadata, db_id, rank) 258 - VALUES ($1, $2, $3, $4, $5, $6) 259 - ON CONFLICT (db_id,chksum) DO UPDATE 260 - SET metadata=$4, rank=$6, name=$1, update_dt=CURRENT_TIMESTAMP 261 - WHERE f.db_id=$5 AND f.chksum=$3 AND f.len=$2 262 - RETURNING exists(SELECT 1 FROM funcs WHERE chksum=$3) 263 - "#).await?; 264 - 265 - debug_assert_eq!(scores.len(), funcs.funcs.len()); 266 - 267 - let mut res = Vec::with_capacity(funcs.funcs.len()); 268 - 269 - // NOTE: Do not access self.conn/prepare_cached before dropping tx - it will deadlock! 270 - { 271 - let mut tx = self.conn.write().await; 272 - let tx = tx.transaction().await?; 273 - 274 - for (func, &score) in funcs.funcs.iter().zip(scores.iter()) { 275 - let name = func.name; 276 - let len = func.func_len as i32; 277 - let chksum = func.hash; 278 - let md = func.func_data; 279 - let score = score as i32; 280 - let row_exists = tx.query_one(&stmt, &[ 281 - &name, &len, &chksum, &md, &db_id, &score 282 - ]).await?; 283 - 284 - let row_exists: bool = row_exists.try_get(0)?; 285 - 286 - res.push(!row_exists); 287 - } 288 - 289 - tx.commit().await?; 290 - } 291 - 292 - Ok(res) 293 - } 294 - 295 - pub async fn is_online(&self) -> bool { 296 - let read = self.conn.read().await; 297 - !read.is_closed() 298 - } 299 - 300 - pub async fn reconnect(&self) -> Result<(), tokio_postgres::Error> { 301 - let connection = Self::connect(self.config.get_config()).await?; 302 - 303 - let conn = &mut *self.conn.write().await; 304 - 305 - self.cache.write().await.clear(); 306 - 307 - *conn = connection; 308 - Ok(()) 309 - } 310 - 311 - pub async fn get_stats(&self) -> Result<DbStats, tokio_postgres::Error> { 312 - let stmt = self.prepare_cached(r#" 313 - SELECT 314 - (SELECT COUNT(*)::int FROM users) as users, 315 - (SELECT COUNT(distinct lic_id)::int FROM users) as hosts, 316 - (SELECT COUNT(distinct chksum)::int FROM funcs) as funcs, 317 - (SELECT COUNT(*)::int FROM funcs) as total_funcs, 318 - (SELECT COUNT(*)::int FROM dbs) as dbs, 319 - (SELECT COUNT(*)::int FROM files) as files 320 - "#).await?; 321 - let db = self.conn.read().await; 322 - let row = db.query_one(&stmt, &[]).await?; 323 - 324 - Ok(DbStats { 325 - unique_lics: row.try_get(0)?, 326 - unique_hosts_per_lic: row.try_get(1)?, 327 - unique_funcs: row.try_get(2)?, 328 - total_funcs: row.try_get(3)?, 329 - dbs: row.try_get(4)?, 330 - unique_files: row.try_get(5)?, 331 - }) 332 - } 333 - 334 - pub async fn get_file_funcs(&self, md5: &[u8], offset: i64, limit: i64) -> Result<Vec<(String, u32, [u8; 16])>, tokio_postgres::Error> { 335 - let stmt = self.prepare_cached(r#" 336 - SELECT fns.name, fns.len, fns.chksum FROM funcs AS fns 337 - LEFT JOIN dbs AS d ON (d.id=fns.db_id) 338 - LEFT JOIN files AS f ON (d.file_id=f.id) 339 - WHERE 340 - f.chksum=$1 341 - LIMIT $2 342 - OFFSET $3 343 - "#).await?; 344 - let db = self.conn.read().await; 345 - let rows = db.query(&stmt, &[&md5, &limit, &offset]).await?; 346 - 347 - let res = rows.into_iter() 348 - .map(|row| { 349 - let name: String = row.get(0); 350 - let len: i32 = row.get(1); 351 - let md5_a: Vec<u8> = row.get(2); 352 - 353 - let mut md5 = [0u8; 16]; 354 - md5.copy_from_slice(&md5_a); 355 - 356 - (name, len as u32, md5) 357 - }) 358 - .collect(); 359 - Ok(res) 360 - } 361 - 362 - pub async fn get_files_with_func(&self, func: &[u8]) -> Result<Vec<[u8; 16]>, tokio_postgres::Error> { 363 - let stmt = self.prepare_cached(r#" 364 - SELECT DISTINCT f.chksum FROM files f 365 - LEFT JOIN dbs d ON (d.file_id = f.id) 366 - LEFT JOIN funcs fns ON (fns.db_id = d.id) 367 - WHERE 368 - fns.chksum = $1 369 - "#).await?; 370 - let db = self.conn.read().await; 371 - let rows = db.query(&stmt, &[&func]).await?; 372 - 373 - let res = rows 374 - .into_iter() 375 - .map(|v| { 376 - let mut chksum = [0u8; 16]; 377 - let v: Vec<u8> = v.get(0); 378 - chksum.copy_from_slice(&v); 379 - chksum 380 - }) 381 - .collect(); 382 - Ok(res) 383 - } 384 - } 1 + use crate::async_drop::{AsyncDropGuard, AsyncDropper}; 2 + use log::*; 3 + use postgres_native_tls::MakeTlsConnector; 4 + use serde::Serialize; 5 + use std::collections::HashMap; 6 + use time::OffsetDateTime; 7 + use tokio_postgres::{tls::MakeTlsConnect, NoTls, Socket}; 8 + pub mod schema; 9 + mod schema_auto; 10 + 11 + use diesel::{ 12 + query_builder::{Query, QueryFragment}, 13 + sql_types::{Array, Binary, Integer, VarChar}, 14 + upsert::excluded, 15 + ExpressionMethods, NullableExpressionMethods, QueryDsl, 16 + }; 17 + use diesel_async::{pooled_connection::ManagerConfig, RunQueryDsl}; 18 + 19 + pub type DynConfig = dyn crate::config::HasConfig + Send + Sync; 20 + 21 + pub struct Database { 22 + tls_connector: Option<MakeTlsConnector>, 23 + diesel: diesel_async::pooled_connection::bb8::Pool<diesel_async::AsyncPgConnection>, 24 + dropper: AsyncDropper, 25 + } 26 + 27 + pub struct FunctionInfo { 28 + pub name: String, 29 + pub len: u32, 30 + pub data: Vec<u8>, 31 + pub popularity: u32, 32 + } 33 + 34 + #[derive(Debug, Serialize)] 35 + pub struct DbStats { 36 + unique_lics: i32, 37 + unique_hosts_per_lic: i32, 38 + 39 + unique_funcs: i32, 40 + total_funcs: i32, 41 + 42 + dbs: i32, 43 + unique_files: i32, 44 + } 45 + 46 + impl Database { 47 + pub async fn open(config: &crate::config::Database) -> Result<Self, anyhow::Error> { 48 + let connection_string = config.connection_info.as_str(); 49 + let tls_connector = if config.use_tls { Some(Self::make_tls(config).await) } else { None }; 50 + 51 + let (dropper, worker) = AsyncDropper::new(); 52 + tokio::task::spawn(worker); 53 + 54 + let diesel = Self::make_bb8_pool(connection_string, tls_connector.clone()).await?; 55 + 56 + Ok(Database { tls_connector, dropper, diesel }) 57 + } 58 + 59 + async fn make_pg_client<T>( 60 + db_url: &str, tls: T, 61 + ) -> diesel::result::ConnectionResult<diesel_async::AsyncPgConnection> 62 + where 63 + T: MakeTlsConnect<Socket>, 64 + T::Stream: Send + 'static, 65 + { 66 + let (cli, conn) = tokio_postgres::connect(db_url, tls).await.map_err(|e| { 67 + error!("failed to connect db: {e}"); 68 + diesel::result::ConnectionError::BadConnection(format!("{e}")) 69 + })?; 70 + 71 + tokio::spawn(async move { 72 + if let Err(e) = conn.await { 73 + error!("connection task error: {e}"); 74 + } 75 + }); 76 + 77 + diesel_async::AsyncPgConnection::try_from(cli).await 78 + } 79 + 80 + async fn make_bb8_pool( 81 + db_url: &str, tls: Option<MakeTlsConnector>, 82 + ) -> Result< 83 + diesel_async::pooled_connection::bb8::Pool<diesel_async::AsyncPgConnection>, 84 + anyhow::Error, 85 + > { 86 + let mut config = ManagerConfig::default(); 87 + config.custom_setup = Box::new(move |db_url| { 88 + let tls = tls.clone(); 89 + Box::pin(async move { 90 + if let Some(tls) = tls { 91 + Self::make_pg_client(db_url, tls).await 92 + } else { 93 + Self::make_pg_client(db_url, NoTls).await 94 + } 95 + }) 96 + }); 97 + let cfg = diesel_async::pooled_connection::AsyncDieselConnectionManager::< 98 + diesel_async::AsyncPgConnection, 99 + >::new_with_config(db_url, config); 100 + 101 + let pool = diesel_async::pooled_connection::bb8::Pool::builder() 102 + .min_idle(Some(1)) 103 + .build(cfg) 104 + .await?; 105 + Ok(pool) 106 + } 107 + 108 + async fn make_tls(database: &crate::config::Database) -> MakeTlsConnector { 109 + use native_tls::{Certificate, Identity, TlsConnector}; 110 + 111 + let mut tls_connector = TlsConnector::builder(); 112 + 113 + if let Some(ref client_identity) = database.client_id { 114 + let client_identity = 115 + tokio::fs::read(client_identity).await.expect("failed to read db's client id"); 116 + let client_identity = Identity::from_pkcs12(&client_identity, "") 117 + .expect("failed to load db's client identity (PKCS12)"); 118 + tls_connector.identity(client_identity); 119 + } 120 + 121 + if let Some(ref server_ca) = database.server_ca { 122 + let server_ca = 123 + tokio::fs::read(server_ca).await.expect("failed to read db's server ca"); 124 + let server_ca = 125 + Certificate::from_pem(&server_ca).expect("failed to load db's server ca (PEM)"); 126 + tls_connector.add_root_certificate(server_ca); 127 + } 128 + 129 + let tls_connector = tls_connector 130 + .danger_accept_invalid_hostnames(true) 131 + .build() 132 + .expect("failed to build TlsConnector"); 133 + 134 + MakeTlsConnector::new(tls_connector) 135 + } 136 + 137 + pub async fn get_funcs( 138 + &self, funcs: &[crate::rpc::PullMetadataFunc<'_>], 139 + ) -> Result<Vec<Option<FunctionInfo>>, anyhow::Error> { 140 + let chksums: Vec<&[u8]> = funcs.iter().map(|v| v.mb_hash).collect(); 141 + 142 + let rows: Vec<(String, i32, Vec<u8>, Vec<u8>)> = { 143 + let conn = &mut self.diesel.get().await?; 144 + 145 + let ct = self.cancel_guard(&*conn); 146 + 147 + let res: Vec<_> = BestMds(chksums.as_slice()).get_results::<_>(conn).await?; 148 + ct.consume(); 149 + res 150 + }; 151 + 152 + let mut partial: HashMap<Vec<u8>, FunctionInfo> = rows 153 + .into_iter() 154 + .map(|row| { 155 + let v = FunctionInfo { name: row.0, len: row.1 as u32, data: row.2, popularity: 0 }; 156 + 157 + (row.3, v) 158 + }) 159 + .collect(); 160 + 161 + let results = partial.len(); 162 + 163 + let res: Vec<Option<FunctionInfo>> = 164 + chksums.iter().map(|&chksum| partial.remove(chksum)).collect(); 165 + 166 + trace!("found {}/{} results", results, chksums.len()); 167 + debug_assert_eq!(chksums.len(), res.len()); 168 + Ok(res) 169 + } 170 + 171 + pub async fn get_or_create_user<'a>( 172 + &self, user: &'a crate::rpc::RpcHello<'a>, hostname: &str, 173 + ) -> Result<i32, anyhow::Error> { 174 + use schema::users; 175 + 176 + let conn = &mut self.diesel.get().await?; 177 + 178 + let lic_id = &user.lic_number[..]; 179 + let lic_data = user.license_data; 180 + 181 + let get_user = || { 182 + users::table 183 + .select(users::id) 184 + .filter(users::lic_data.eq(lic_data)) 185 + .filter(users::lic_id.eq(lic_id)) 186 + .filter(users::hostname.eq(hostname)) 187 + }; 188 + 189 + match get_user().get_result::<i32>(conn).await { 190 + Ok(v) => return Ok(v), 191 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 192 + _ => {}, 193 + }; 194 + 195 + match diesel::insert_into(users::table) 196 + .values(vec![( 197 + users::lic_id.eq(lic_id), 198 + users::lic_data.eq(lic_data), 199 + users::hostname.eq(hostname), 200 + )]) 201 + .returning(users::id) // xmax = 0 if the row is new 202 + .get_result::<i32>(conn) 203 + .await 204 + { 205 + Ok(v) => return Ok(v), 206 + Err(diesel::result::Error::DatabaseError( 207 + diesel::result::DatabaseErrorKind::UniqueViolation, 208 + _, 209 + )) => {}, 210 + Err(e) => return Err(e.into()), 211 + } 212 + 213 + Ok(get_user().get_result::<i32>(conn).await?) 214 + } 215 + 216 + async fn get_or_create_file<'a>( 217 + &self, funcs: &'a crate::rpc::PushMetadata<'a>, 218 + ) -> Result<i32, anyhow::Error> { 219 + use schema::files::{chksum, id, table as files}; 220 + 221 + let hash = &funcs.md5[..]; 222 + 223 + let conn = &mut self.diesel.get().await?; 224 + 225 + let get_file = || files.filter(chksum.eq(hash)).select(id); 226 + 227 + match get_file().get_result::<i32>(conn).await { 228 + Ok(v) => return Ok(v), 229 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 230 + _ => {}, 231 + } 232 + 233 + match diesel::insert_into(files) 234 + .values(vec![(chksum.eq(hash),)]) 235 + .returning(id) 236 + .get_result::<i32>(conn) 237 + .await 238 + { 239 + Ok(v) => return Ok(v), 240 + Err(diesel::result::Error::DatabaseError( 241 + diesel::result::DatabaseErrorKind::UniqueViolation, 242 + _, 243 + )) => {}, 244 + Err(e) => return Err(e.into()), 245 + } 246 + Ok(get_file().get_result::<i32>(conn).await?) 247 + } 248 + 249 + async fn get_or_create_db<'a>( 250 + &self, user: &'a crate::rpc::RpcHello<'a>, funcs: &'a crate::rpc::PushMetadata<'a>, 251 + ) -> Result<i32, anyhow::Error> { 252 + use schema::dbs::{ 253 + file_id as db_file_id, file_path, id as db_id, idb_path, table as dbs, 254 + user_id as db_user, 255 + }; 256 + 257 + let file_id = self.get_or_create_file(funcs); 258 + let user_id = self.get_or_create_user(user, funcs.hostname); 259 + 260 + let (file_id, user_id): (i32, i32) = futures_util::try_join!(file_id, user_id)?; 261 + 262 + let conn = &mut self.diesel.get().await?; 263 + 264 + let get_db = || { 265 + dbs.select(db_id) 266 + .filter(db_user.eq(user_id)) 267 + .filter(db_file_id.eq(file_id)) 268 + .filter(file_path.eq(funcs.file_path)) 269 + .filter(idb_path.eq(funcs.idb_path)) 270 + }; 271 + 272 + match get_db().get_result::<i32>(conn).await { 273 + Ok(v) => return Ok(v), 274 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 275 + _ => {}, 276 + }; 277 + 278 + match diesel::insert_into(dbs) 279 + .values(vec![( 280 + db_user.eq(user_id), 281 + db_file_id.eq(file_id), 282 + file_path.eq(funcs.file_path), 283 + idb_path.eq(funcs.idb_path), 284 + )]) 285 + .returning(db_id) 286 + .get_result::<i32>(conn) 287 + .await 288 + { 289 + Ok(id) => return Ok(id), 290 + Err(diesel::result::Error::DatabaseError( 291 + diesel::result::DatabaseErrorKind::UniqueViolation, 292 + _, 293 + )) => {}, 294 + Err(e) => return Err(e.into()), 295 + }; 296 + Ok(get_db().get_result::<i32>(conn).await?) 297 + } 298 + 299 + pub async fn push_funcs<'a, 'b>( 300 + &'b self, user: &'a crate::rpc::RpcHello<'a>, funcs: &'a crate::rpc::PushMetadata<'a>, 301 + scores: &[u32], 302 + ) -> Result<Vec<bool>, anyhow::Error> { 303 + use futures_util::TryStreamExt; 304 + 305 + // postgres has a limitation of binding per statement (i16::MAX). Split large push requests into smaller chunks. 306 + const PUSH_FUNC_CHUNK_SIZE: usize = 3000; 307 + 308 + let db_id = self.get_or_create_db(user, funcs).await?; 309 + 310 + let mut rows = Vec::with_capacity(funcs.funcs.len().min(PUSH_FUNC_CHUNK_SIZE)); 311 + let mut is_new = Vec::with_capacity(funcs.funcs.len()); 312 + let conn = &mut self.diesel.get().await?; 313 + let f2 = diesel::alias!(schema::funcs as f2); 314 + 315 + for (idx, (func, &score)) in funcs.funcs.iter().zip(scores.iter()).enumerate() { 316 + let name = func.name; 317 + let len = func.func_len as i32; 318 + let chksum = func.hash; 319 + let md = func.func_data; 320 + let score = score as i32; 321 + 322 + rows.push(( 323 + schema::funcs::name.eq(name), 324 + schema::funcs::len.eq(len), 325 + schema::funcs::chksum.eq(chksum), 326 + schema::funcs::metadata.eq(md), 327 + schema::funcs::rank.eq(score), 328 + schema::funcs::db_id.eq(db_id), 329 + )); 330 + 331 + if rows.len() < PUSH_FUNC_CHUNK_SIZE && idx < funcs.funcs.len() - 1 { 332 + continue; 333 + } 334 + 335 + let mut current_rows = 336 + Vec::with_capacity((funcs.funcs.len() - (idx + 1)).max(PUSH_FUNC_CHUNK_SIZE)); 337 + std::mem::swap(&mut current_rows, &mut rows); 338 + 339 + diesel::insert_into(schema::funcs::table) 340 + .values(current_rows) 341 + .on_conflict((schema::funcs::chksum, schema::funcs::db_id)) 342 + .do_update() 343 + .set(( 344 + schema::funcs::name.eq(excluded(schema::funcs::name)), 345 + schema::funcs::metadata.eq(excluded(schema::funcs::metadata)), 346 + schema::funcs::rank.eq(excluded(schema::funcs::rank)), 347 + schema::funcs::update_dt.eq(diesel::dsl::now), 348 + )) 349 + .returning(diesel::dsl::not(diesel::dsl::exists( 350 + f2.filter(f2.field(schema::funcs::chksum).eq(schema::funcs::chksum)), 351 + ))) // xmax=0 when a new row is created. 352 + .load_stream::<bool>(conn) 353 + .await? 354 + .try_fold(&mut is_new, |acc, item: bool| { 355 + acc.push(item); 356 + futures_util::future::ready(Ok(acc)) 357 + }) 358 + .await?; 359 + } 360 + 361 + Ok(is_new) 362 + } 363 + 364 + pub async fn get_file_funcs( 365 + &self, md5: &[u8], offset: i64, limit: i64, 366 + ) -> Result<Vec<(String, i32, Vec<u8>)>, anyhow::Error> { 367 + let conn = &mut self.diesel.get().await?; 368 + let results = schema::funcs::table 369 + .left_join(schema::dbs::table.left_join(schema::files::table)) 370 + .select(( 371 + schema::funcs::name.assume_not_null(), 372 + schema::funcs::len.assume_not_null(), 373 + schema::funcs::chksum.assume_not_null(), 374 + )) 375 + .filter(schema::files::chksum.eq(md5)) 376 + .offset(offset) 377 + .limit(limit) 378 + .get_results::<(String, i32, Vec<u8>)>(conn) 379 + .await?; 380 + Ok(results) 381 + } 382 + 383 + pub async fn get_files_with_func(&self, func: &[u8]) -> Result<Vec<Vec<u8>>, anyhow::Error> { 384 + let conn = &mut self.diesel.get().await?; 385 + 386 + let res = schema::files::table 387 + .left_join(schema::dbs::table.left_join(schema::funcs::table)) 388 + .select(schema::files::chksum.assume_not_null()) 389 + .distinct() 390 + .filter(schema::funcs::chksum.eq(func)) 391 + .get_results::<Vec<u8>>(conn) 392 + .await?; 393 + Ok(res) 394 + } 395 + 396 + fn cancel_guard( 397 + &self, 398 + conn: &diesel_async::pooled_connection::bb8::PooledConnection< 399 + '_, 400 + diesel_async::AsyncPgConnection, 401 + >, 402 + ) -> AsyncDropGuard { 403 + let token = conn.cancel_token(); 404 + let tls_connector = self.tls_connector.clone(); 405 + self.dropper.defer(async move { 406 + debug!("cancelling query..."); 407 + 408 + if let Some(tls) = tls_connector { 409 + let _ = token.cancel_query(tls).await; 410 + } else { 411 + let _ = token.cancel_query(NoTls).await; 412 + } 413 + }) 414 + } 415 + 416 + pub async fn delete_metadata( 417 + &self, req: &crate::rpc::DelHistory<'_>, 418 + ) -> Result<(), anyhow::Error> { 419 + use schema::funcs::{chksum, table as funcs}; 420 + 421 + let chksums = req.funcs.iter().map(|v| v.as_slice()).collect::<Vec<_>>(); 422 + 423 + let conn = &mut self.diesel.get().await?; 424 + let rows_modified = 425 + diesel::delete(funcs.filter(chksum.eq_any(&chksums))).execute(conn).await?; 426 + 427 + debug!("deleted {rows_modified} rows"); 428 + 429 + Ok(()) 430 + } 431 + 432 + pub async fn get_func_histories( 433 + &self, chksum: &[u8], limit: u32, 434 + ) -> Result<Vec<(OffsetDateTime, String, Vec<u8>)>, anyhow::Error> { 435 + let conn = &mut self.diesel.get().await?; 436 + let rows = &schema::funcs::table.select(( 437 + schema::funcs::update_dt.assume_not_null(), 438 + schema::funcs::name, 439 + schema::funcs::metadata.assume_not_null(), 440 + )); 441 + let rows = rows 442 + .limit(limit as i64) 443 + .order_by(schema::funcs::update_dt.desc()) 444 + .filter(schema::funcs::chksum.eq(chksum)) 445 + .get_results::<(time::OffsetDateTime, String, Vec<u8>)>(conn) 446 + .await?; 447 + Ok(rows) 448 + } 449 + } 450 + 451 + // This is eww, but it's the fastest. 452 + struct BestMds<'a>(&'a [&'a [u8]]); 453 + impl<'a> QueryFragment<diesel::pg::Pg> for BestMds<'a> { 454 + fn walk_ast<'b>( 455 + &'b self, mut pass: diesel::query_builder::AstPass<'_, 'b, diesel::pg::Pg>, 456 + ) -> diesel::QueryResult<()> { 457 + pass.push_sql( 458 + r#"WITH best AS ( 459 + select chksum,MAX(rank) as maxrank from funcs f1 460 + WHERE chksum = ANY("#, 461 + ); 462 + pass.push_bind_param::<Array<Binary>, _>(&self.0)?; 463 + pass.push_sql( 464 + r#") 465 + GROUP BY chksum 466 + ) 467 + SELECT f2.name,f2.len,f2.metadata,f2.chksum FROM best 468 + LEFT JOIN funcs f2 ON (best.chksum=f2.chksum AND best.maxrank=f2.rank)"#, 469 + ); 470 + Ok(()) 471 + } 472 + } 473 + impl<'a> diesel::query_builder::QueryId for BestMds<'a> { 474 + type QueryId = BestMds<'static>; 475 + } 476 + impl<'a> Query for BestMds<'a> { 477 + type SqlType = (VarChar, Integer, Binary, Binary); 478 + }
+17
common/src/db/schema.rs
··· 1 + pub use super::schema_auto::*; 2 + 3 + diesel::table! { 4 + func_ranks { 5 + id -> Int4, 6 + name -> Text, 7 + len -> Int4, 8 + db_id -> Int4, 9 + chksum -> Nullable<Bytea>, 10 + metadata -> Nullable<Bytea>, 11 + rank -> Nullable<Int4>, 12 + push_dt -> Nullable<Timestamptz>, 13 + update_dt -> Nullable<Timestamptz>, 14 + } 15 + } 16 + 17 + diesel::joinable!(func_ranks -> dbs (id));
+48
common/src/db/schema_auto.rs
··· 1 + // @generated automatically by Diesel CLI. 2 + 3 + diesel::table! { 4 + dbs (id) { 5 + id -> Int4, 6 + file_path -> Nullable<Varchar>, 7 + idb_path -> Nullable<Varchar>, 8 + file_id -> Nullable<Int4>, 9 + user_id -> Nullable<Int4>, 10 + } 11 + } 12 + 13 + diesel::table! { 14 + files (id) { 15 + id -> Int4, 16 + chksum -> Nullable<Bytea>, 17 + } 18 + } 19 + 20 + diesel::table! { 21 + funcs (id) { 22 + id -> Int4, 23 + name -> Text, 24 + len -> Int4, 25 + db_id -> Int4, 26 + chksum -> Nullable<Bytea>, 27 + metadata -> Nullable<Bytea>, 28 + rank -> Nullable<Int4>, 29 + push_dt -> Nullable<Timestamptz>, 30 + update_dt -> Nullable<Timestamptz>, 31 + } 32 + } 33 + 34 + diesel::table! { 35 + users (id) { 36 + id -> Int4, 37 + lic_id -> Nullable<Bytea>, 38 + lic_data -> Nullable<Bytea>, 39 + hostname -> Nullable<Varchar>, 40 + first_seen -> Nullable<Timestamptz>, 41 + } 42 + } 43 + 44 + diesel::joinable!(dbs -> files (file_id)); 45 + diesel::joinable!(dbs -> users (user_id)); 46 + diesel::joinable!(funcs -> dbs (db_id)); 47 + 48 + diesel::allow_tables_to_appear_in_same_query!(dbs, files, funcs, users,);
+10 -3
common/src/lib.rs
··· 3 3 4 4 use std::fmt::Write; 5 5 6 - pub mod db; 6 + pub mod async_drop; 7 7 pub mod config; 8 + pub mod db; 8 9 pub mod md; 10 + pub mod metrics; 9 11 pub mod rpc; 10 12 pub mod web; 11 13 ··· 13 15 pub db: db::Database, 14 16 pub config: std::sync::Arc<config::Config>, 15 17 pub server_name: String, 18 + pub metrics: metrics::Metrics, 16 19 } 17 20 18 21 pub type SharedState = std::sync::Arc<SharedState_>; ··· 22 25 const CHUNK_SIZE: usize = 32; 23 26 data.chunks(CHUNK_SIZE).for_each(|chunk| { 24 27 for &ch in chunk { 25 - let _ = write!(&mut output, "{:02x} ", ch); 28 + let _ = write!(&mut output, "{ch:02x} "); 26 29 } 27 30 let padding = CHUNK_SIZE - chunk.len(); 28 31 for _ in 0..padding { ··· 30 33 } 31 34 32 35 let _ = write!(&mut output, " | "); 33 - for ch in chunk.iter().chain(std::iter::repeat(&b' ').take(padding)).map(|&v| std::char::from_u32(v as u32).unwrap_or('.')) { 36 + for ch in chunk 37 + .iter() 38 + .chain(std::iter::repeat(&b' ').take(padding)) 39 + .map(|&v| std::char::from_u32(v as u32).unwrap_or('.')) 40 + { 34 41 if !ch.is_ascii_graphic() { 35 42 output.push('.'); 36 43 } else {
+218 -214
common/src/md.rs
··· 1 - use std::collections::HashSet; 2 - 3 - use serde::{Serialize, Deserialize}; 4 - use crate::rpc::de::from_slice; 5 - 6 - #[derive(Serialize, Deserialize)] 7 - pub struct MetadataChunk<'a> { 8 - code: u32, 9 - data: &'a [u8], 10 - } 11 - 12 - #[derive(Debug)] 13 - pub enum FunctionMetadata<'a> { 14 - FunctionComment(FunctionComment<'a>), 15 - ByteComment(ByteComment<'a>), 16 - ExtraComment(ExtraComment<'a>), 17 - } 18 - 19 - #[derive(Debug)] 20 - pub struct FunctionComment<'a> { 21 - pub is_repeatable: bool, 22 - pub comment: &'a str, 23 - } 24 - 25 - #[derive(Debug)] 26 - pub struct ByteComment<'a> { 27 - pub is_repeatable: bool, 28 - pub offset: u32, 29 - pub comment: &'a str, 30 - } 31 - 32 - #[derive(Debug)] 33 - pub struct ExtraComment<'a> { 34 - pub offset: u32, 35 - pub anterior: &'a str, 36 - pub posterior: &'a str, 37 - } 38 - 39 - impl<'a> FunctionMetadata<'a> { 40 - fn is_useful(&self) -> bool { 41 - // TODO: rewrite using regex with configurable library names 42 - match self { 43 - FunctionMetadata::ExtraComment(cmt) => { 44 - if cmt.anterior.starts_with("; Exported entry ") // offset=0 45 - { 46 - return false; 47 - } 48 - if cmt.anterior.is_empty() && cmt.posterior.is_empty() { 49 - return false; 50 - } 51 - }, 52 - FunctionMetadata::FunctionComment(cmt) => { 53 - if cmt.comment == "Microsoft VisualC v14 64bit runtime" 54 - || cmt.comment == "Microsoft VisualC 64bit universal runtime" 55 - { 56 - return false; 57 - } 58 - if cmt.comment.is_empty() { 59 - return false; 60 - } 61 - }, 62 - FunctionMetadata::ByteComment(cmt) => { 63 - if cmt.comment == "Trap to Debugger" 64 - || (cmt.comment.starts_with("jumptable ") && cmt.comment.contains(" case")) // repeatable=true 65 - || cmt.comment == "switch jump" 66 - || (cmt.comment.starts_with("switch ") && cmt.comment.ends_with(" cases ")) 67 - || cmt.comment == "jump table for switch statement" 68 - || cmt.comment == "indirect table for switch statement" 69 - || cmt.comment == "Microsoft VisualC v7/14 64bit runtime" 70 - || cmt.comment == "Microsoft VisualC v7/14 64bit runtime\nMicrosoft VisualC v14 64bit runtime" 71 - || cmt.comment == "Microsoft VisualC v14 64bit runtime" { 72 - return false; 73 - } 74 - if cmt.comment.is_empty() { 75 - return false; 76 - } 77 - }, 78 - } 79 - true 80 - } 81 - } 82 - 83 - fn deserialize_seq<'de, T: Deserialize<'de>>(mut data: &'de [u8]) -> Result<Vec<(u32, T)>, crate::rpc::Error> { 84 - let mut res = vec![]; 85 - let mut reset = true; 86 - let (mut offset, used): (u32, usize) = from_slice(data)?; 87 - data = &data[used..]; 88 - if data.is_empty() { 89 - return Err(crate::rpc::Error::UnexpectedEof); 90 - } 91 - 92 - loop { 93 - let (offset_diff, used): (u32, usize) = from_slice(data)?; 94 - data = &data[used..]; 95 - if data.is_empty() { 96 - return Err(crate::rpc::Error::UnexpectedEof); 97 - } 98 - 99 - if (offset_diff > 0) || reset { 100 - offset += offset_diff; 101 - let (e, used): (T, usize) = from_slice(data)?; 102 - data = &data[used..]; 103 - 104 - res.push((offset, e)); 105 - 106 - reset = false; 107 - } else { 108 - let (offset_diff, used): (u32, usize) = from_slice(data)?; 109 - data = &data[used..]; 110 - 111 - offset = offset_diff; 112 - reset = true; 113 - } 114 - 115 - if data.is_empty() { 116 - break; 117 - } 118 - } 119 - 120 - Ok(res) 121 - } 122 - 123 - pub fn parse_metadata(mut data: &[u8]) -> Result<Vec<FunctionMetadata<'_>>, crate::rpc::Error> { 124 - let mut res = vec![]; 125 - let mut bad_codes = HashSet::new(); 126 - 127 - while !data.is_empty() { 128 - let (chunk, used) :(MetadataChunk, _) = from_slice(data)?; 129 - data = &data[used..]; 130 - 131 - let data = chunk.data; 132 - 133 - if data.is_empty() { 134 - continue; 135 - } 136 - 137 - match chunk.code { 138 - 1 => {}, // TODO: parse typeinfo 139 - 2 => {}, // nop 140 - 3 | 4 => { // function comments 141 - let is_repeatable = chunk.code == 4; 142 - let cmt = std::str::from_utf8(data)?; 143 - res.push(FunctionMetadata::FunctionComment(FunctionComment{ 144 - is_repeatable, 145 - comment: cmt, 146 - })); 147 - }, 148 - 5 | 6 => { // comments 149 - let is_repeatable = chunk.code == 6; 150 - let byte_comments: Vec<(_, &[u8])> = match deserialize_seq(data) { 151 - Ok(v) => v, 152 - Err(err) => { 153 - log::error!("err: {}\n{}", err, super::make_pretty_hex(data)); 154 - return Err(err); 155 - }, 156 - }; 157 - 158 - for comment in byte_comments { 159 - let cmt = std::str::from_utf8(comment.1)?; 160 - res.push(FunctionMetadata::ByteComment( 161 - ByteComment{ 162 - is_repeatable, 163 - offset: comment.0, 164 - comment: cmt, 165 - } 166 - )); 167 - } 168 - }, 169 - 7 => { // extra comments 170 - let byte_comments: Vec<(_, (&[u8], &[u8]))> = match deserialize_seq(data) { 171 - Ok(v) => v, 172 - Err(err) => { 173 - log::error!("err: {}\n{}", err, super::make_pretty_hex(data)); 174 - return Err(err); 175 - }, 176 - }; 177 - 178 - for comment in byte_comments { 179 - res.push(FunctionMetadata::ExtraComment(ExtraComment{ 180 - offset: comment.0, 181 - anterior: std::str::from_utf8(comment.1.0)?, 182 - posterior: std::str::from_utf8(comment.1.1)?, 183 - })); 184 - } 185 - }, 186 - 9 | 10 => { /* TODO! */ }, 187 - _ => { 188 - bad_codes.insert(chunk.code); 189 - }, 190 - } 191 - } 192 - 193 - Ok(res) 194 - } 195 - 196 - pub fn get_score(md: &crate::rpc::PushMetadataFunc) -> u32 { 197 - let mut score = 0; 198 - 199 - let md = match parse_metadata(md.func_data) { 200 - Ok(v) => v, 201 - Err(e) => { 202 - log::warn!("failed to parse metadata: {}", e); 203 - return 0; 204 - } 205 - }; 206 - 207 - for md in md { 208 - if md.is_useful() { 209 - score += 10; 210 - } 211 - } 212 - 213 - score 214 - } 1 + use std::collections::HashSet; 2 + 3 + use crate::rpc::de::from_slice; 4 + use serde::{Deserialize, Serialize}; 5 + 6 + #[derive(Serialize, Deserialize)] 7 + pub struct MetadataChunk<'a> { 8 + code: u32, 9 + data: &'a [u8], 10 + } 11 + 12 + #[derive(Debug)] 13 + pub enum FunctionMetadata<'a> { 14 + FunctionComment(FunctionComment<'a>), 15 + ByteComment(ByteComment<'a>), 16 + ExtraComment(ExtraComment<'a>), 17 + } 18 + 19 + #[derive(Debug)] 20 + pub struct FunctionComment<'a> { 21 + pub is_repeatable: bool, 22 + pub comment: &'a str, 23 + } 24 + 25 + #[derive(Debug)] 26 + pub struct ByteComment<'a> { 27 + pub is_repeatable: bool, 28 + pub offset: u32, 29 + pub comment: &'a str, 30 + } 31 + 32 + #[derive(Debug)] 33 + pub struct ExtraComment<'a> { 34 + pub offset: u32, 35 + pub anterior: &'a str, 36 + pub posterior: &'a str, 37 + } 38 + 39 + impl<'a> FunctionMetadata<'a> { 40 + fn is_useful(&self) -> bool { 41 + // TODO: rewrite using regex with configurable library names 42 + match self { 43 + FunctionMetadata::ExtraComment(cmt) => { 44 + if cmt.anterior.starts_with("; Exported entry ") 45 + // offset=0 46 + { 47 + return false; 48 + } 49 + if cmt.anterior.is_empty() && cmt.posterior.is_empty() { 50 + return false; 51 + } 52 + }, 53 + FunctionMetadata::FunctionComment(cmt) => { 54 + if cmt.comment == "Microsoft VisualC v14 64bit runtime" 55 + || cmt.comment == "Microsoft VisualC 64bit universal runtime" 56 + { 57 + return false; 58 + } 59 + if cmt.comment.is_empty() { 60 + return false; 61 + } 62 + }, 63 + FunctionMetadata::ByteComment(cmt) => { 64 + if cmt.comment == "Trap to Debugger" 65 + || (cmt.comment.starts_with("jumptable ") && cmt.comment.contains(" case")) // repeatable=true 66 + || cmt.comment == "switch jump" 67 + || (cmt.comment.starts_with("switch ") && cmt.comment.ends_with(" cases ")) 68 + || cmt.comment == "jump table for switch statement" 69 + || cmt.comment == "indirect table for switch statement" 70 + || cmt.comment == "Microsoft VisualC v7/14 64bit runtime" 71 + || cmt.comment == "Microsoft VisualC v7/14 64bit runtime\nMicrosoft VisualC v14 64bit runtime" 72 + || cmt.comment == "Microsoft VisualC v14 64bit runtime" { 73 + return false; 74 + } 75 + if cmt.comment.is_empty() { 76 + return false; 77 + } 78 + }, 79 + } 80 + true 81 + } 82 + } 83 + 84 + fn deserialize_seq<'de, T: Deserialize<'de>>( 85 + mut data: &'de [u8], 86 + ) -> Result<Vec<(u32, T)>, crate::rpc::Error> { 87 + let mut res = vec![]; 88 + let mut reset = true; 89 + let (mut offset, used): (u32, usize) = from_slice(data)?; 90 + data = &data[used..]; 91 + if data.is_empty() { 92 + return Err(crate::rpc::Error::UnexpectedEof); 93 + } 94 + 95 + loop { 96 + let (offset_diff, used): (u32, usize) = from_slice(data)?; 97 + data = &data[used..]; 98 + if data.is_empty() { 99 + return Err(crate::rpc::Error::UnexpectedEof); 100 + } 101 + 102 + if (offset_diff > 0) || reset { 103 + offset += offset_diff; 104 + let (e, used): (T, usize) = from_slice(data)?; 105 + data = &data[used..]; 106 + 107 + res.push((offset, e)); 108 + 109 + reset = false; 110 + } else { 111 + let (offset_diff, used): (u32, usize) = from_slice(data)?; 112 + data = &data[used..]; 113 + 114 + offset = offset_diff; 115 + reset = true; 116 + } 117 + 118 + if data.is_empty() { 119 + break; 120 + } 121 + } 122 + 123 + Ok(res) 124 + } 125 + 126 + pub fn parse_metadata(mut data: &[u8]) -> Result<Vec<FunctionMetadata<'_>>, crate::rpc::Error> { 127 + let mut res = vec![]; 128 + let mut bad_codes = HashSet::new(); 129 + 130 + while !data.is_empty() { 131 + let (chunk, used): (MetadataChunk, _) = from_slice(data)?; 132 + data = &data[used..]; 133 + 134 + let data = chunk.data; 135 + 136 + if data.is_empty() { 137 + continue; 138 + } 139 + 140 + match chunk.code { 141 + 1 => {}, // TODO: parse typeinfo 142 + 2 => {}, // nop 143 + 3 | 4 => { 144 + // function comments 145 + let is_repeatable = chunk.code == 4; 146 + let cmt = std::str::from_utf8(data)?; 147 + res.push(FunctionMetadata::FunctionComment(FunctionComment { 148 + is_repeatable, 149 + comment: cmt, 150 + })); 151 + }, 152 + 5 | 6 => { 153 + // comments 154 + let is_repeatable = chunk.code == 6; 155 + let byte_comments: Vec<(_, &[u8])> = match deserialize_seq(data) { 156 + Ok(v) => v, 157 + Err(err) => { 158 + log::error!("err: {}\n{}", err, super::make_pretty_hex(data)); 159 + return Err(err); 160 + }, 161 + }; 162 + 163 + for comment in byte_comments { 164 + let cmt = std::str::from_utf8(comment.1)?; 165 + res.push(FunctionMetadata::ByteComment(ByteComment { 166 + is_repeatable, 167 + offset: comment.0, 168 + comment: cmt, 169 + })); 170 + } 171 + }, 172 + 7 => { 173 + // extra comments 174 + let byte_comments: Vec<(_, (&[u8], &[u8]))> = match deserialize_seq(data) { 175 + Ok(v) => v, 176 + Err(err) => { 177 + log::error!("err: {}\n{}", err, super::make_pretty_hex(data)); 178 + return Err(err); 179 + }, 180 + }; 181 + 182 + for comment in byte_comments { 183 + res.push(FunctionMetadata::ExtraComment(ExtraComment { 184 + offset: comment.0, 185 + anterior: std::str::from_utf8(comment.1 .0)?, 186 + posterior: std::str::from_utf8(comment.1 .1)?, 187 + })); 188 + } 189 + }, 190 + 9 | 10 => { /* TODO! */ }, 191 + _ => { 192 + bad_codes.insert(chunk.code); 193 + }, 194 + } 195 + } 196 + 197 + Ok(res) 198 + } 199 + 200 + pub fn get_score(md: &crate::rpc::PushMetadataFunc) -> u32 { 201 + let mut score = 0; 202 + 203 + let md = match parse_metadata(md.func_data) { 204 + Ok(v) => v, 205 + Err(e) => { 206 + log::warn!("failed to parse metadata: {}", e); 207 + return 0; 208 + }, 209 + }; 210 + 211 + for md in md { 212 + if md.is_useful() { 213 + score += 10; 214 + } 215 + } 216 + 217 + score 218 + }
+80
common/src/metrics.rs
··· 1 + use std::sync::atomic::AtomicI64; 2 + 3 + use prometheus_client::{ 4 + encoding::EncodeLabelSet, 5 + metrics::{counter::Counter, family::Family, gauge::Gauge}, 6 + registry::Registry, 7 + }; 8 + 9 + pub struct Metrics { 10 + pub registry: Registry, 11 + 12 + /// Count active lumina connections 13 + pub active_connections: Gauge<i64, AtomicI64>, 14 + 15 + /// Record connected client versions 16 + pub lumina_version: Family<LuminaVersion, Gauge>, 17 + 18 + /// Count new functions pushes 19 + pub new_funcs: Counter<u64>, 20 + 21 + /// Count pushed functions 22 + pub pushes: Counter<u64>, 23 + 24 + /// Count pulled functions (only found) 25 + pub pulls: Counter<u64>, 26 + 27 + /// Queried functions 28 + pub queried_funcs: Counter<u64>, 29 + } 30 + 31 + #[derive(EncodeLabelSet, Debug, Hash, Eq, PartialEq, Clone)] 32 + pub struct LuminaVersion { 33 + pub protocol_version: u32, 34 + } 35 + 36 + impl Default for Metrics { 37 + fn default() -> Self { 38 + let mut registry = Registry::default(); 39 + 40 + let active_connections = Gauge::default(); 41 + registry.register( 42 + "lumen_active_connections", 43 + "Active Lumina connections", 44 + active_connections.clone(), 45 + ); 46 + 47 + let lumina_version = Family::<LuminaVersion, Gauge>::default(); 48 + registry.register( 49 + "lumen_protocol_version", 50 + "Version of Lumina protocol being used", 51 + lumina_version.clone(), 52 + ); 53 + 54 + let new_funcs = Counter::default(); 55 + registry.register( 56 + "lumen_new_funcs", 57 + "Pushes previously unknown functions", 58 + new_funcs.clone(), 59 + ); 60 + 61 + let pushes = Counter::default(); 62 + registry.register("lumen_pushes_total", "Total pushes functions", pushes.clone()); 63 + 64 + let pulls = Counter::default(); 65 + registry.register("lumen_pulls_total", "Total pulled functions", pulls.clone()); 66 + 67 + let queried_funcs = Counter::default(); 68 + registry.register("lumen_queried_total", "Total Queried functions", queried_funcs.clone()); 69 + 70 + Metrics { 71 + registry, 72 + active_connections, 73 + lumina_version, 74 + new_funcs, 75 + pushes, 76 + pulls, 77 + queried_funcs, 78 + } 79 + } 80 + }
+163 -159
common/src/rpc/de.rs
··· 1 - use serde::Deserialize; 2 - use serde::de::{self, DeserializeSeed, SeqAccess, Visitor}; 3 - use super::Error; 4 - 5 - struct Deserializer<'de> { 6 - input: &'de [u8], 7 - } 8 - 9 - impl<'de> Deserializer<'de> { 10 - fn from_bytes(b: &'de [u8]) -> Self { 11 - Self { 12 - input: b, 13 - } 14 - } 15 - 16 - fn unpack_dd(&mut self) -> Result<u32, Error> { 17 - let (v, len) = super::packing::unpack_dd(self.input); 18 - if len == 0 { 19 - Err(Error::UnexpectedEof) 20 - } else { 21 - self.input = &self.input[len..]; 22 - Ok(v) 23 - } 24 - } 25 - 26 - fn unpack_dq(&mut self) -> Result<u64, Error> { 27 - let a = self.unpack_dd()? as u64; 28 - let b = self.unpack_dd()? as u64; 29 - Ok((a << 32) | b) 30 - } 31 - 32 - fn unpack_var_bytes(&mut self) -> Result<&'de [u8], Error> { 33 - let bytes = self.unpack_dd()? as usize; 34 - if bytes > self.input.len() { 35 - return Err(Error::UnexpectedEof); 36 - } 37 - 38 - let payload = &self.input[..bytes]; 39 - self.input = &self.input[bytes..]; 40 - assert_eq!(payload.len(), bytes); 41 - 42 - Ok(payload) 43 - } 44 - 45 - fn unpack_cstr(&mut self) -> Result<&'de str, Error> { 46 - let len = self.input.iter().enumerate().find_map(|(idx, &v)| if v == 0 { Some(idx) } else { None }); 47 - let len = match len { 48 - Some(v) => v, 49 - None => return Err(Error::UnexpectedEof), 50 - }; 51 - let res = match std::str::from_utf8(&self.input[..len]) { 52 - Ok(v) => v, 53 - Err(err) => { 54 - return Err(err.into()); 55 - } 56 - }; 57 - self.input = &self.input[len + 1..]; 58 - Ok(res) 59 - } 60 - 61 - fn take_byte(&mut self) -> Result<u8, Error> { 62 - if self.input.is_empty() { 63 - return Err(Error::UnexpectedEof); 64 - } 65 - 66 - let v = self.input[0]; 67 - self.input = &self.input[1..]; 68 - Ok(v) 69 - } 70 - } 71 - 72 - /// Returns: a tuple containing the deserialized struct and the bytes used 73 - pub fn from_slice<'a, T: Deserialize<'a>>(b: &'a [u8]) -> Result<(T, usize), Error> { 74 - let mut de = Deserializer::from_bytes(b); 75 - let v = T::deserialize(&mut de)?; 76 - Ok((v, b.len() - de.input.len())) 77 - } 78 - 79 - impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { 80 - type Error = Error; 81 - 82 - fn deserialize_any<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> { 83 - unimplemented!() 84 - } 85 - 86 - fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 87 - visitor.visit_u8(self.take_byte()?) 88 - } 89 - 90 - fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 91 - visitor.visit_u32(self.unpack_dd()?) 92 - } 93 - 94 - fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 95 - visitor.visit_u64(self.unpack_dq()?) 96 - } 97 - 98 - fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 99 - let len = self.unpack_dd()?; 100 - 101 - visitor.visit_seq(Access { 102 - len: len as usize, 103 - de: &mut *self, 104 - }) 105 - } 106 - 107 - fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 108 - visitor.visit_borrowed_str(self.unpack_cstr()?) 109 - } 110 - 111 - fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 112 - let v = self.unpack_var_bytes()?; 113 - visitor.visit_borrowed_bytes(v) 114 - } 115 - 116 - fn deserialize_tuple<V: Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error> { 117 - visitor.visit_seq(Access { 118 - len, 119 - de: &mut *self, 120 - }) 121 - } 122 - 123 - fn deserialize_tuple_struct<V: Visitor<'de>>(self, _name: &'static str, len: usize, visitor: V) -> Result<V::Value, Self::Error> { 124 - self.deserialize_tuple(len, visitor) 125 - } 126 - 127 - fn deserialize_struct<V: Visitor<'de>>(self, name: &'static str, fields: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error> { 128 - self.deserialize_tuple_struct(name, fields.len(), visitor) 129 - } 130 - 131 - serde::forward_to_deserialize_any! { 132 - i8 i16 i32 i64 char u16 bool 133 - f32 f64 string byte_buf option unit unit_struct newtype_struct map enum identifier ignored_any 134 - } 135 - } 136 - 137 - struct Access<'a, 'de> { 138 - de: &'a mut Deserializer<'de>, 139 - len: usize, 140 - } 141 - 142 - impl<'de, 'a> SeqAccess<'a> for Access<'de, 'a> { 143 - type Error = Error; 144 - 145 - fn next_element_seed<T: DeserializeSeed<'a>>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> { 146 - if self.len > 0 { 147 - self.len -= 1; 148 - 149 - let v = serde::de::DeserializeSeed::deserialize(seed, &mut *self.de)?; 150 - Ok(Some(v)) 151 - } else { 152 - Ok(None) 153 - } 154 - } 155 - 156 - fn size_hint(&self) -> Option<usize> { 157 - Some(self.len) 158 - } 159 - } 1 + use super::Error; 2 + use serde::de::{self, DeserializeSeed, SeqAccess, Visitor}; 3 + use serde::Deserialize; 4 + 5 + struct Deserializer<'de> { 6 + input: &'de [u8], 7 + } 8 + 9 + impl<'de> Deserializer<'de> { 10 + fn from_bytes(b: &'de [u8]) -> Self { 11 + Self { input: b } 12 + } 13 + 14 + fn unpack_dd(&mut self) -> Result<u32, Error> { 15 + let (v, len) = super::packing::unpack_dd(self.input); 16 + if len == 0 { 17 + Err(Error::UnexpectedEof) 18 + } else { 19 + self.input = &self.input[len..]; 20 + Ok(v) 21 + } 22 + } 23 + 24 + fn unpack_dq(&mut self) -> Result<u64, Error> { 25 + let a = self.unpack_dd()? as u64; 26 + let b = self.unpack_dd()? as u64; 27 + Ok((a << 32) | b) 28 + } 29 + 30 + fn unpack_var_bytes(&mut self) -> Result<&'de [u8], Error> { 31 + let bytes = self.unpack_dd()? as usize; 32 + if bytes > self.input.len() { 33 + return Err(Error::UnexpectedEof); 34 + } 35 + 36 + let payload = &self.input[..bytes]; 37 + self.input = &self.input[bytes..]; 38 + assert_eq!(payload.len(), bytes); 39 + 40 + Ok(payload) 41 + } 42 + 43 + fn unpack_cstr(&mut self) -> Result<&'de str, Error> { 44 + let len = self 45 + .input 46 + .iter() 47 + .enumerate() 48 + .find_map(|(idx, &v)| if v == 0 { Some(idx) } else { None }); 49 + let len = match len { 50 + Some(v) => v, 51 + None => return Err(Error::UnexpectedEof), 52 + }; 53 + let res = match std::str::from_utf8(&self.input[..len]) { 54 + Ok(v) => v, 55 + Err(err) => { 56 + return Err(err.into()); 57 + }, 58 + }; 59 + self.input = &self.input[len + 1..]; 60 + Ok(res) 61 + } 62 + 63 + fn take_byte(&mut self) -> Result<u8, Error> { 64 + if self.input.is_empty() { 65 + return Err(Error::UnexpectedEof); 66 + } 67 + 68 + let v = self.input[0]; 69 + self.input = &self.input[1..]; 70 + Ok(v) 71 + } 72 + } 73 + 74 + /// Returns: a tuple containing the deserialized struct and the bytes used 75 + pub fn from_slice<'a, T: Deserialize<'a>>(b: &'a [u8]) -> Result<(T, usize), Error> { 76 + let mut de = Deserializer::from_bytes(b); 77 + let v = T::deserialize(&mut de)?; 78 + Ok((v, b.len() - de.input.len())) 79 + } 80 + 81 + impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { 82 + type Error = Error; 83 + 84 + fn deserialize_any<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> { 85 + unimplemented!() 86 + } 87 + 88 + fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 89 + visitor.visit_u8(self.take_byte()?) 90 + } 91 + 92 + fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 93 + visitor.visit_u32(self.unpack_dd()?) 94 + } 95 + 96 + fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 97 + visitor.visit_u64(self.unpack_dq()?) 98 + } 99 + 100 + fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 101 + let len = self.unpack_dd()?; 102 + 103 + visitor.visit_seq(Access { len: len as usize, de: &mut *self }) 104 + } 105 + 106 + fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 107 + visitor.visit_borrowed_str(self.unpack_cstr()?) 108 + } 109 + 110 + fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { 111 + let v = self.unpack_var_bytes()?; 112 + visitor.visit_borrowed_bytes(v) 113 + } 114 + 115 + fn deserialize_tuple<V: Visitor<'de>>( 116 + self, len: usize, visitor: V, 117 + ) -> Result<V::Value, Self::Error> { 118 + visitor.visit_seq(Access { len, de: &mut *self }) 119 + } 120 + 121 + fn deserialize_tuple_struct<V: Visitor<'de>>( 122 + self, _name: &'static str, len: usize, visitor: V, 123 + ) -> Result<V::Value, Self::Error> { 124 + self.deserialize_tuple(len, visitor) 125 + } 126 + 127 + fn deserialize_struct<V: Visitor<'de>>( 128 + self, name: &'static str, fields: &'static [&'static str], visitor: V, 129 + ) -> Result<V::Value, Self::Error> { 130 + self.deserialize_tuple_struct(name, fields.len(), visitor) 131 + } 132 + 133 + serde::forward_to_deserialize_any! { 134 + i8 i16 i32 i64 char u16 bool 135 + f32 f64 string byte_buf option unit unit_struct newtype_struct map enum identifier ignored_any 136 + } 137 + } 138 + 139 + struct Access<'a, 'de> { 140 + de: &'a mut Deserializer<'de>, 141 + len: usize, 142 + } 143 + 144 + impl<'de, 'a> SeqAccess<'a> for Access<'de, 'a> { 145 + type Error = Error; 146 + 147 + fn next_element_seed<T: DeserializeSeed<'a>>( 148 + &mut self, seed: T, 149 + ) -> Result<Option<T::Value>, Self::Error> { 150 + if self.len > 0 { 151 + self.len -= 1; 152 + 153 + let v = serde::de::DeserializeSeed::deserialize(seed, &mut *self.de)?; 154 + Ok(Some(v)) 155 + } else { 156 + Ok(None) 157 + } 158 + } 159 + 160 + fn size_hint(&self) -> Option<usize> { 161 + Some(self.len) 162 + } 163 + }
+156 -86
common/src/rpc/messages.rs
··· 1 - use serde::{Serialize, Deserialize}; 2 - use std::borrow::Cow; 3 - 4 - #[derive(Deserialize, Serialize)] 5 - pub struct RpcFail<'a> { 6 - pub code: u32, 7 - pub message: &'a str, 8 - } 9 - 10 - #[derive(Serialize, Deserialize)] 11 - pub struct RpcNotify<'a> { 12 - pub code: u32, 13 - pub msg: &'a str, 14 - } 15 - 16 - #[derive(Serialize, Deserialize, Debug)] 17 - pub struct Creds<'a> { 18 - pub username: &'a str, 19 - pub password: &'a str, 20 - } 21 - 22 - #[derive(Serialize, Deserialize)] 23 - pub struct RpcHello<'a> { 24 - pub protocol_version: u32, 25 - pub license_data: &'a [u8], 26 - pub lic_number: [u8; 6], 27 - pub unk2: u32, 28 - } 29 - 30 - #[derive(Deserialize, Serialize, Clone)] 31 - pub struct PullMetadataFunc<'a> { 32 - pub unk0: u32, 33 - pub mb_hash: &'a [u8], 34 - } 35 - 36 - #[derive(Deserialize, Serialize)] 37 - pub struct PullMetadata<'a> { 38 - pub unk0: u32, 39 - pub unk1: Cow<'a, [u32]>, 40 - 41 - #[serde(borrow)] 42 - pub funcs: Cow<'a, [PullMetadataFunc<'a>]>, 43 - } 44 - 45 - #[derive(Deserialize, Serialize, Clone)] 46 - pub struct PullMetadataResultFunc<'a> { 47 - pub name: Cow<'a, str>, 48 - pub len: u32, 49 - pub mb_data: Cow<'a, [u8]>, 50 - pub popularity: u32, 51 - } 52 - 53 - #[derive(Deserialize, Serialize)] 54 - pub struct PullMetadataResult<'a> { 55 - pub unk0: Cow<'a, [u32]>, 56 - #[serde(borrow)] 57 - pub funcs: Cow<'a, [PullMetadataResultFunc<'a>]>, 58 - } 59 - 60 - #[derive(Clone, Deserialize, Serialize)] 61 - pub struct PushMetadataFunc<'a> { 62 - pub name: &'a str, 63 - pub func_len: u32, 64 - pub func_data: &'a [u8], 65 - 66 - // PullMetadata's fields (tuple 'unk2') are similar to these two 67 - pub unk2: u32, 68 - pub hash: &'a [u8], 69 - } 70 - 71 - #[derive(Deserialize, Serialize)] 72 - pub struct PushMetadata<'a> { 73 - pub unk0: u32, 74 - pub idb_path: &'a str, 75 - pub file_path: &'a str, 76 - pub md5: [u8; 16], 77 - pub hostname: &'a str, 78 - pub funcs: Cow<'a, [PushMetadataFunc<'a>]>, 79 - pub unk1: Cow<'a, [u64]>, 80 - } 81 - 82 - #[derive(Deserialize, Serialize)] 83 - pub struct PushMetadataResult<'a> { 84 - // array of 0=exists, 1=NEW 85 - pub status: Cow<'a, [u32]>, 86 - } 1 + use serde::{Deserialize, Serialize}; 2 + use std::borrow::Cow; 3 + 4 + #[derive(Deserialize, Serialize)] 5 + pub struct RpcFail<'a> { 6 + pub code: u32, 7 + pub message: &'a str, 8 + } 9 + 10 + #[derive(Serialize, Deserialize)] 11 + pub struct RpcNotify<'a> { 12 + pub code: u32, 13 + pub msg: &'a str, 14 + } 15 + 16 + #[derive(Serialize, Deserialize, Debug)] 17 + pub struct Creds<'a> { 18 + pub username: &'a str, 19 + pub password: &'a str, 20 + } 21 + 22 + #[derive(Serialize, Deserialize)] 23 + pub struct RpcHello<'a> { 24 + pub protocol_version: u32, 25 + pub license_data: &'a [u8], 26 + pub lic_number: [u8; 6], 27 + pub unk2: u32, 28 + } 29 + 30 + #[derive(Debug, Deserialize, Serialize, Clone)] 31 + pub struct PullMetadataFunc<'a> { 32 + pub unk0: u32, 33 + pub mb_hash: &'a [u8], 34 + } 35 + 36 + #[derive(Deserialize, Serialize)] 37 + pub struct PullMetadata<'a> { 38 + pub unk0: u32, 39 + pub unk1: Cow<'a, [u32]>, 40 + 41 + #[serde(borrow)] 42 + pub funcs: Cow<'a, [PullMetadataFunc<'a>]>, 43 + } 44 + 45 + #[derive(Deserialize, Serialize, Clone)] 46 + pub struct PullMetadataResultFunc<'a> { 47 + pub name: Cow<'a, str>, 48 + pub len: u32, 49 + pub mb_data: Cow<'a, [u8]>, 50 + pub popularity: u32, 51 + } 52 + 53 + #[derive(Deserialize, Serialize)] 54 + pub struct PullMetadataResult<'a> { 55 + pub unk0: Cow<'a, [u32]>, 56 + #[serde(borrow)] 57 + pub funcs: Cow<'a, [PullMetadataResultFunc<'a>]>, 58 + } 59 + 60 + #[derive(Clone, Deserialize, Serialize)] 61 + pub struct PushMetadataFunc<'a> { 62 + pub name: &'a str, 63 + pub func_len: u32, 64 + pub func_data: &'a [u8], 65 + 66 + // PullMetadata's fields (tuple 'unk2') are similar to these two 67 + pub unk2: u32, 68 + pub hash: &'a [u8], 69 + } 70 + 71 + #[derive(Deserialize, Serialize)] 72 + pub struct PushMetadata<'a> { 73 + pub unk0: u32, 74 + pub idb_path: &'a str, 75 + pub file_path: &'a str, 76 + pub md5: [u8; 16], 77 + pub hostname: &'a str, 78 + pub funcs: Cow<'a, [PushMetadataFunc<'a>]>, 79 + pub unk1: Cow<'a, [u64]>, 80 + } 81 + 82 + #[derive(Deserialize, Serialize)] 83 + pub struct PushMetadataResult<'a> { 84 + // array of 0=exists, 1=NEW 85 + pub status: Cow<'a, [u32]>, 86 + } 87 + 88 + #[derive(Debug, Deserialize, Serialize)] 89 + pub struct DelHistory<'a> { 90 + pub unk0: u32, // =0x08 91 + pub unk1: Cow<'a, [Cow<'a, str>]>, 92 + pub unk2: Cow<'a, [[u64; 2]]>, 93 + pub unk3: Cow<'a, [[u64; 2]]>, 94 + pub unk4: Cow<'a, [Cow<'a, str>]>, 95 + pub unk5: Cow<'a, [Cow<'a, str>]>, 96 + pub unk6: Cow<'a, [Cow<'a, str>]>, 97 + pub unk7: Cow<'a, [Cow<'a, str>]>, 98 + pub unk8: Cow<'a, [Cow<'a, [u8; 16]>]>, 99 + pub funcs: Cow<'a, [Cow<'a, [u8; 16]>]>, 100 + pub unk10: Cow<'a, [[u64; 2]]>, 101 + pub unk11: u64, 102 + } 103 + 104 + #[derive(Deserialize, Serialize)] 105 + pub struct DelHistoryResult { 106 + pub deleted_mds: u32, 107 + } 108 + 109 + #[derive(Debug, Deserialize, Serialize, Default)] 110 + pub struct LicenseInfo<'a> { 111 + pub id: Cow<'a, str>, 112 + pub name: Cow<'a, str>, 113 + pub email: Cow<'a, str>, 114 + } 115 + 116 + #[derive(Debug, Deserialize, Serialize, Default)] 117 + pub struct HelloResult<'a> { 118 + pub license_info: LicenseInfo<'a>, 119 + pub username: Cow<'a, str>, 120 + pub karma: u32, 121 + pub last_active: u64, 122 + pub features: u32, 123 + } 124 + 125 + #[derive(Debug, Deserialize, Serialize)] 126 + pub struct GetFuncHistories<'a> { 127 + #[serde(borrow)] 128 + pub funcs: Cow<'a, [PullMetadataFunc<'a>]>, 129 + pub unk0: u32, 130 + } 131 + 132 + #[derive(Debug, Deserialize, Serialize, Clone)] 133 + pub struct FunctionHistory<'a> { 134 + pub unk0: u64, 135 + pub unk1: u64, 136 + pub name: Cow<'a, str>, 137 + pub metadata: Cow<'a, [u8]>, 138 + pub timestamp: u64, 139 + pub author_idx: u32, 140 + pub idb_path_idx: u32, 141 + } 142 + 143 + #[derive(Debug, Deserialize, Serialize, Clone)] 144 + pub struct FunctionHistories<'a> { 145 + #[serde(borrow)] 146 + pub log: Cow<'a, [FunctionHistory<'a>]>, 147 + } 148 + 149 + #[derive(Debug, Deserialize, Serialize)] 150 + pub struct GetFuncHistoriesResult<'a> { 151 + pub status: Cow<'a, [u32]>, 152 + #[serde(borrow)] 153 + pub funcs: Cow<'a, [FunctionHistories<'a>]>, 154 + pub users: Cow<'a, [Cow<'a, str>]>, 155 + pub dbs: Cow<'a, [Cow<'a, str>]>, 156 + }
+257 -228
common/src/rpc/mod.rs
··· 1 - use tokio::io::{AsyncWriteExt, AsyncWrite, AsyncRead, AsyncReadExt}; 2 - use log::*; 3 - mod ser; 4 - pub(crate) mod de; 5 - mod messages; 6 - mod packing; 7 - use serde::Serializer; 8 - 9 - pub use messages::*; 10 - 11 - #[derive(Debug)] 12 - pub enum Error { 13 - UnexpectedEof, 14 - Utf8Error(std::str::Utf8Error), 15 - IOError(std::io::Error), 16 - Serde(String), 17 - DbError(tokio_postgres::Error), 18 - InvalidData, 19 - OutOfMemory, 20 - Todo, 21 - Timeout, 22 - Eof, 23 - } 24 - 25 - impl std::fmt::Display for Error { 26 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 - std::fmt::Debug::fmt(self, f) 28 - } 29 - } 30 - 31 - impl std::error::Error for Error {} 32 - impl serde::ser::Error for Error { 33 - fn custom<T: std::fmt::Display>(msg: T) -> Self { 34 - Error::Serde(msg.to_string()) 35 - } 36 - } 37 - impl serde::de::Error for Error { 38 - fn custom<T: std::fmt::Display>(msg: T) -> Self { 39 - Error::Serde(msg.to_string()) 40 - } 41 - } 42 - impl From<std::io::Error> for Error { 43 - fn from(v: std::io::Error) -> Self { 44 - Error::IOError(v) 45 - } 46 - } 47 - impl From<std::str::Utf8Error> for Error { 48 - fn from(v: std::str::Utf8Error) -> Self { 49 - Error::Utf8Error(v) 50 - } 51 - } 52 - impl From<tokio_postgres::Error> for Error { 53 - fn from(v: tokio_postgres::Error) -> Self { 54 - Error::DbError(v) 55 - } 56 - } 57 - impl From<std::collections::TryReserveError> for Error { 58 - fn from(v: std::collections::TryReserveError) -> Self { 59 - error!("failed to allocate {} bytes", v); 60 - Error::OutOfMemory 61 - } 62 - } 63 - 64 - fn get_code_maxlen(code: u8) -> usize { 65 - match code { 66 - 0x0e => 50 * 1024 * 1024, // PullMD: 50 MiB 67 - 0x10 => 200 * 1024 * 1024, // PushMD: 200 MiB 68 - _ => 1024 * 50, // otherwise 50K 69 - } 70 - } 71 - 72 - pub async fn read_packet<R: AsyncRead + Unpin>(mut reader: R) -> Result<Vec<u8>, Error> { 73 - let mut head = [0u8; 5]; 74 - match reader.read_exact(&mut head).await { 75 - Ok(_) => {}, 76 - Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Err(Error::Eof), // client decided to disconnect... 77 - Err(e) => return Err(e.into()), 78 - } 79 - let code = head[4]; 80 - let mut buf_len = [0u8; 4]; 81 - buf_len.copy_from_slice(&head[..4]); 82 - 83 - let buf_len = u32::from_be_bytes(buf_len) as usize; 84 - if buf_len < 4 { 85 - return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "payload size is too small").into()); 86 - } 87 - 88 - let max_len = get_code_maxlen(code); 89 - 90 - if buf_len > max_len { 91 - info!("maxium size exceeded: code={}: max={}; req={}", code, max_len, buf_len); 92 - return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "request length exceeded maximum limit").into()); 93 - } 94 - 95 - // the additional byte is for the RPC code 96 - trace!("expecting {} bytes...", buf_len); 97 - let buf_len = buf_len + 1; 98 - 99 - let mut data = Vec::new(); 100 - data.try_reserve_exact(buf_len)?; 101 - data.resize(buf_len, 0); 102 - data[0] = code; 103 - reader.read_exact(&mut data[1..]).await?; 104 - 105 - Ok(data) 106 - } 107 - 108 - async fn write_packet<W: AsyncWrite + Unpin>(mut w: W, data: &[u8]) -> Result<(), std::io::Error> { 109 - let buf_len: u32 = (data.len() - 1) as u32; 110 - let buf_len = buf_len.to_be_bytes(); 111 - w.write_all(&buf_len).await?; 112 - w.write_all(data).await?; 113 - Ok(()) 114 - } 115 - 116 - pub enum RpcMessage<'a> { 117 - Ok(()), 118 - Fail(RpcFail<'a>), 119 - Notify(RpcNotify<'a>), 120 - Hello(RpcHello<'a>, Option<Creds<'a>>), 121 - PullMetadata(PullMetadata<'a>), 122 - PullMetadataResult(PullMetadataResult<'a>), 123 - PushMetadata(PushMetadata<'a>), 124 - PushMetadataResult(PushMetadataResult<'a>), 125 - } 126 - 127 - impl<'a> serde::Serialize for RpcMessage<'a> { 128 - fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 129 - use serde::ser::SerializeTuple; 130 - 131 - let code = self.get_code(); 132 - let mut tuple = serializer.serialize_tuple(2)?; 133 - 134 - // u8 is pushed without further encoding... 135 - tuple.serialize_element(&code)?; 136 - 137 - match self { 138 - RpcMessage::Ok(msg) => tuple.serialize_element(msg)?, 139 - RpcMessage::Fail(msg) => tuple.serialize_element(msg)?, 140 - RpcMessage::Notify(msg) => tuple.serialize_element(msg)?, 141 - RpcMessage::Hello(msg, _) => tuple.serialize_element(msg)?, 142 - RpcMessage::PullMetadata(msg) => tuple.serialize_element(msg)?, 143 - RpcMessage::PullMetadataResult(msg) => tuple.serialize_element(msg)?, 144 - RpcMessage::PushMetadata(msg) => tuple.serialize_element(msg)?, 145 - RpcMessage::PushMetadataResult(msg) => tuple.serialize_element(msg)?, 146 - } 147 - 148 - tuple.end() 149 - } 150 - } 151 - 152 - impl<'a> RpcMessage<'a> { 153 - fn deserialize_check<T: serde::Deserialize<'a>>(payload: &'a [u8]) -> Result<T, Error> { 154 - let v = de::from_slice(payload)?; 155 - if v.1 != payload.len() { 156 - let bytes_remaining = crate::make_pretty_hex(&payload[v.1..]); 157 - trace!("{} remaining bytes after deserializing {}\n{bytes_remaining}", payload.len() - v.1, std::any::type_name::<T>()); 158 - } 159 - Ok(v.0) 160 - } 161 - 162 - pub fn deserialize(payload: &'a [u8]) -> Result<RpcMessage<'a>, Error> { 163 - let msg_type = payload[0]; 164 - let payload = &payload[1..]; 165 - 166 - let res = match msg_type { 167 - 0x0a => { 168 - if !payload.is_empty() { 169 - trace!("Ok message with additional data: {} bytes: {payload:02x?}", payload.len()); 170 - } 171 - RpcMessage::Ok(()) 172 - }, 173 - 0x0b => RpcMessage::Fail(Self::deserialize_check(payload)?), 174 - 0x0c => RpcMessage::Notify(Self::deserialize_check(payload)?), 175 - 0x0d => { 176 - let (hello, consumed) = de::from_slice::<messages::RpcHello>(payload)?; 177 - let creds = if payload.len() > consumed && hello.protocol_version > 2 { 178 - let payload = &payload[consumed..]; 179 - let (creds, consumed) = de::from_slice::<Creds>(payload)?; 180 - if payload.len() != consumed { 181 - trace!("bytes remaining after HelloV2: {payload:02x?}"); 182 - } 183 - Some(creds) 184 - } else { 185 - if hello.protocol_version > 2 || payload.len() != consumed { 186 - trace!("Unexpected Hello msg: {payload:02x?}"); 187 - } 188 - None 189 - }; 190 - RpcMessage::Hello(hello, creds) 191 - }, 192 - 0x0e => RpcMessage::PullMetadata(Self::deserialize_check(payload)?), 193 - 0x0f => RpcMessage::PullMetadataResult(Self::deserialize_check(payload)?), 194 - 0x10 => RpcMessage::PushMetadata(Self::deserialize_check(payload)?), 195 - 0x11 => RpcMessage::PushMetadataResult(Self::deserialize_check(payload)?), 196 - _ => { 197 - trace!("got invalid message type '{:02x}'", msg_type); 198 - return Err(Error::InvalidData); 199 - }, 200 - }; 201 - 202 - Ok(res) 203 - } 204 - 205 - pub async fn async_write<W: AsyncWrite + Unpin>(&self, w: W) -> Result<(), Error> { 206 - let mut output = Vec::with_capacity(32); 207 - ser::to_writer(self, &mut output)?; 208 - 209 - write_packet(w, &output).await?; 210 - 211 - Ok(()) 212 - } 213 - 214 - fn get_code(&self) -> u8 { 215 - use RpcMessage::*; 216 - 217 - match self { 218 - Ok(_) => 0x0a, 219 - Fail(_) => 0x0b, 220 - Notify(_) => 0x0c, 221 - Hello(..) => 0x0d, 222 - PullMetadata(_) => 0x0e, 223 - PullMetadataResult(_) => 0x0f, 224 - PushMetadata(_) => 0x10, 225 - PushMetadataResult(_) => 0x11, 226 - } 227 - } 228 - } 1 + use log::*; 2 + use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 3 + pub(crate) mod de; 4 + mod messages; 5 + mod packing; 6 + mod ser; 7 + use serde::Serializer; 8 + 9 + pub use messages::*; 10 + 11 + #[derive(Debug)] 12 + pub enum Error { 13 + UnexpectedEof, 14 + Utf8Error(std::str::Utf8Error), 15 + IOError(std::io::Error), 16 + Serde(String), 17 + InvalidData, 18 + OutOfMemory, 19 + Todo, 20 + Timeout, 21 + Eof, 22 + } 23 + 24 + impl std::fmt::Display for Error { 25 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 26 + std::fmt::Debug::fmt(self, f) 27 + } 28 + } 29 + 30 + impl std::error::Error for Error {} 31 + impl serde::ser::Error for Error { 32 + fn custom<T: std::fmt::Display>(msg: T) -> Self { 33 + Error::Serde(msg.to_string()) 34 + } 35 + } 36 + impl serde::de::Error for Error { 37 + fn custom<T: std::fmt::Display>(msg: T) -> Self { 38 + Error::Serde(msg.to_string()) 39 + } 40 + } 41 + impl From<std::io::Error> for Error { 42 + fn from(v: std::io::Error) -> Self { 43 + Error::IOError(v) 44 + } 45 + } 46 + impl From<std::str::Utf8Error> for Error { 47 + fn from(v: std::str::Utf8Error) -> Self { 48 + Error::Utf8Error(v) 49 + } 50 + } 51 + impl From<std::collections::TryReserveError> for Error { 52 + fn from(v: std::collections::TryReserveError) -> Self { 53 + error!("failed to allocate {} bytes", v); 54 + Error::OutOfMemory 55 + } 56 + } 57 + 58 + fn get_code_maxlen(code: u8) -> usize { 59 + match code { 60 + 0x0e => 50 * 1024 * 1024, // PullMD: 50 MiB 61 + 0x10 => 200 * 1024 * 1024, // PushMD: 200 MiB 62 + _ => 1024 * 50, // otherwise 50K 63 + } 64 + } 65 + 66 + pub async fn read_packet<R: AsyncRead + Unpin>(mut reader: R) -> Result<Vec<u8>, Error> { 67 + let mut head = [0u8; 5]; 68 + match reader.read_exact(&mut head).await { 69 + Ok(_) => {}, 70 + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Err(Error::Eof), // client decided to disconnect... 71 + Err(e) => return Err(e.into()), 72 + } 73 + let code = head[4]; 74 + let mut buf_len = [0u8; 4]; 75 + buf_len.copy_from_slice(&head[..4]); 76 + 77 + let buf_len = u32::from_be_bytes(buf_len) as usize; 78 + if buf_len < 4 { 79 + return Err(std::io::Error::new( 80 + std::io::ErrorKind::InvalidData, 81 + "payload size is too small", 82 + ) 83 + .into()); 84 + } 85 + 86 + let max_len = get_code_maxlen(code); 87 + 88 + if buf_len > max_len { 89 + info!("maxium size exceeded: code={}: max={}; req={}", code, max_len, buf_len); 90 + return Err(std::io::Error::new( 91 + std::io::ErrorKind::InvalidData, 92 + "request length exceeded maximum limit", 93 + ) 94 + .into()); 95 + } 96 + 97 + // the additional byte is for the RPC code 98 + trace!("expecting {} bytes...", buf_len); 99 + let buf_len = buf_len + 1; 100 + 101 + let mut data = Vec::new(); 102 + data.try_reserve_exact(buf_len)?; 103 + data.resize(buf_len, 0); 104 + data[0] = code; 105 + reader.read_exact(&mut data[1..]).await?; 106 + 107 + Ok(data) 108 + } 109 + 110 + async fn write_packet<W: AsyncWrite + Unpin>(mut w: W, data: &[u8]) -> Result<(), std::io::Error> { 111 + let buf_len: u32 = (data.len() - 1) as u32; 112 + let buf_len = buf_len.to_be_bytes(); 113 + w.write_all(&buf_len).await?; 114 + w.write_all(data).await?; 115 + Ok(()) 116 + } 117 + 118 + pub enum RpcMessage<'a> { 119 + Ok(()), 120 + Fail(RpcFail<'a>), 121 + Notify(RpcNotify<'a>), 122 + Hello(RpcHello<'a>, Option<Creds<'a>>), 123 + PullMetadata(PullMetadata<'a>), 124 + PullMetadataResult(PullMetadataResult<'a>), 125 + PushMetadata(PushMetadata<'a>), 126 + PushMetadataResult(PushMetadataResult<'a>), 127 + DelHistory(DelHistory<'a>), 128 + DelHistoryResult(DelHistoryResult), 129 + GetFuncHistories(GetFuncHistories<'a>), 130 + GetFuncHistoriesResult(GetFuncHistoriesResult<'a>), 131 + HelloResult(HelloResult<'a>), 132 + } 133 + 134 + impl<'a> serde::Serialize for RpcMessage<'a> { 135 + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 136 + use serde::ser::SerializeTuple; 137 + 138 + let code = self.get_code(); 139 + let mut tuple = serializer.serialize_tuple(2)?; 140 + 141 + // u8 is pushed without further encoding... 142 + tuple.serialize_element(&code)?; 143 + 144 + match self { 145 + RpcMessage::Ok(msg) => tuple.serialize_element(msg)?, 146 + RpcMessage::Fail(msg) => tuple.serialize_element(msg)?, 147 + RpcMessage::Notify(msg) => tuple.serialize_element(msg)?, 148 + RpcMessage::Hello(msg, _) => tuple.serialize_element(msg)?, 149 + RpcMessage::PullMetadata(msg) => tuple.serialize_element(msg)?, 150 + RpcMessage::PullMetadataResult(msg) => tuple.serialize_element(msg)?, 151 + RpcMessage::PushMetadata(msg) => tuple.serialize_element(msg)?, 152 + RpcMessage::PushMetadataResult(msg) => tuple.serialize_element(msg)?, 153 + RpcMessage::DelHistory(msg) => tuple.serialize_element(msg)?, 154 + RpcMessage::DelHistoryResult(msg) => tuple.serialize_element(msg)?, 155 + RpcMessage::GetFuncHistories(msg) => tuple.serialize_element(msg)?, 156 + RpcMessage::GetFuncHistoriesResult(msg) => tuple.serialize_element(msg)?, 157 + RpcMessage::HelloResult(msg) => tuple.serialize_element(msg)?, 158 + } 159 + 160 + tuple.end() 161 + } 162 + } 163 + 164 + impl<'a> RpcMessage<'a> { 165 + fn deserialize_check<T: serde::Deserialize<'a>>(payload: &'a [u8]) -> Result<T, Error> { 166 + let v = de::from_slice(payload)?; 167 + if v.1 != payload.len() { 168 + let bytes_remaining = crate::make_pretty_hex(&payload[v.1..]); 169 + trace!( 170 + "{} remaining bytes after deserializing {}\n{bytes_remaining}", 171 + payload.len() - v.1, 172 + std::any::type_name::<T>() 173 + ); 174 + } 175 + Ok(v.0) 176 + } 177 + 178 + pub fn deserialize(payload: &'a [u8]) -> Result<RpcMessage<'a>, Error> { 179 + let msg_type = payload[0]; 180 + let payload = &payload[1..]; 181 + 182 + let res = match msg_type { 183 + 0x0a => { 184 + if !payload.is_empty() { 185 + trace!( 186 + "Ok message with additional data: {} bytes: {payload:02x?}", 187 + payload.len() 188 + ); 189 + } 190 + RpcMessage::Ok(()) 191 + }, 192 + 0x0b => RpcMessage::Fail(Self::deserialize_check(payload)?), 193 + 0x0c => RpcMessage::Notify(Self::deserialize_check(payload)?), 194 + 0x0d => { 195 + let (hello, consumed) = de::from_slice::<messages::RpcHello>(payload)?; 196 + let creds = if payload.len() > consumed && hello.protocol_version > 2 { 197 + let payload = &payload[consumed..]; 198 + let (creds, consumed) = de::from_slice::<Creds>(payload)?; 199 + if payload.len() != consumed { 200 + trace!("bytes remaining after HelloV2: {payload:02x?}"); 201 + } 202 + Some(creds) 203 + } else { 204 + if hello.protocol_version > 2 || payload.len() != consumed { 205 + trace!("Unexpected Hello msg: {payload:02x?}"); 206 + } 207 + None 208 + }; 209 + RpcMessage::Hello(hello, creds) 210 + }, 211 + 0x0e => RpcMessage::PullMetadata(Self::deserialize_check(payload)?), 212 + 0x0f => RpcMessage::PullMetadataResult(Self::deserialize_check(payload)?), 213 + 0x10 => RpcMessage::PushMetadata(Self::deserialize_check(payload)?), 214 + 0x11 => RpcMessage::PushMetadataResult(Self::deserialize_check(payload)?), 215 + 0x18 => RpcMessage::DelHistory(Self::deserialize_check(payload)?), 216 + 0x19 => RpcMessage::DelHistoryResult(Self::deserialize_check(payload)?), 217 + 0x2f => RpcMessage::GetFuncHistories(Self::deserialize_check(payload)?), 218 + 0x30 => RpcMessage::GetFuncHistoriesResult(Self::deserialize_check(payload)?), 219 + 0x31 => RpcMessage::HelloResult(Self::deserialize_check(payload)?), 220 + _ => { 221 + trace!("got invalid message type '{:02x}'", msg_type); 222 + return Err(Error::InvalidData); 223 + }, 224 + }; 225 + 226 + Ok(res) 227 + } 228 + 229 + pub async fn async_write<W: AsyncWrite + Unpin>(&self, w: W) -> Result<(), Error> { 230 + let mut output = Vec::with_capacity(32); 231 + ser::to_writer(self, &mut output)?; 232 + 233 + write_packet(w, &output).await?; 234 + 235 + Ok(()) 236 + } 237 + 238 + fn get_code(&self) -> u8 { 239 + use RpcMessage::*; 240 + 241 + match self { 242 + Ok(_) => 0x0a, 243 + Fail(_) => 0x0b, 244 + Notify(_) => 0x0c, 245 + Hello(..) => 0x0d, 246 + PullMetadata(_) => 0x0e, 247 + PullMetadataResult(_) => 0x0f, 248 + PushMetadata(_) => 0x10, 249 + PushMetadataResult(_) => 0x11, 250 + DelHistory(_) => 0x18, 251 + DelHistoryResult(_) => 0x19, 252 + GetFuncHistories(_) => 0x2f, 253 + GetFuncHistoriesResult(_) => 0x30, 254 + HelloResult(_) => 0x31, 255 + } 256 + } 257 + }
+111 -104
common/src/rpc/packing.rs
··· 1 - /// packs a dd into `buf` returning amout of bytes written. 2 - /// Returns 0 if buffer is too small 3 - pub fn pack_dd(v: u32, buf: &mut [u8]) -> usize { 4 - let bytes = v.to_le_bytes(); 5 - match v { 6 - 0..=0x7f => { // 0..0XXXXXXX (7 bits) 7 - if buf.is_empty() { 8 - return 0; 9 - } 10 - buf[0] = bytes[0]; 11 - 1 12 - }, 13 - 0x80..=0x3fff => { // 10AAAAAA..BBBBBBBB (14 bits) 14 - if buf.len() < 2 { 15 - return 0; 16 - } 17 - buf[0] = 0x80 | bytes[1]; 18 - buf[1] = bytes[0]; 19 - 2 20 - }, 21 - 0x4000..=0x1fffff => { // 11000000_AAAAAAAA_BBBBBBBB_CCCCCCCC (24 bits) 22 - if buf.len() < 3 { 23 - return 0; 24 - } 25 - buf[0] = 0xc0; 26 - buf[1] = bytes[2]; 27 - buf[2] = bytes[1]; 28 - buf[3] = bytes[0]; 29 - 4 30 - }, 31 - 0x200000..=u32::MAX => { // 11111111_AAAAAAAA_BBBBBBBB_CCCCCCCC_DDDDDDDD (32 bits) 32 - if buf.len() < 5 { 33 - return 0; 34 - } 35 - buf[0] = 0xff; 36 - buf[1] = bytes[3]; 37 - buf[2] = bytes[2]; 38 - buf[3] = bytes[1]; 39 - buf[4] = bytes[0]; 40 - 5 41 - } 42 - } 43 - } 44 - 45 - /// unpacks a dd from `buf`, returning the amount (value, byte consumed) 46 - pub fn unpack_dd(buf: &[u8]) -> (u32, usize) { 47 - if buf.is_empty() { 48 - return (0, 0); 49 - } 50 - 51 - let msb = buf[0]; 52 - let mut val = [0u8; 4]; 53 - 54 - if msb & 0x80 == 0 { // 0...... 55 - val[0] = msb; 56 - return (u32::from_le_bytes(val), 1); 57 - } 58 - if msb & 0x40 == 0 { // 10....../0x80 59 - if buf.len() < 2 { 60 - return (0, 0); 61 - } 62 - val[1] = msb & 0x3f; 63 - val[0] = buf[1]; 64 - return (u32::from_le_bytes(val), 2); 65 - } 66 - if msb & 0x20 == 0 { // 110...../0xC0 67 - if buf.len() < 4 { 68 - return (0, 0); 69 - } 70 - val[3] = msb & 0x1f; 71 - val[2] = buf[1]; 72 - val[1] = buf[2]; 73 - val[0] = buf[3]; 74 - return (u32::from_le_bytes(val), 4); 75 - } 76 - 77 - if buf.len() < 5 { 78 - return (0, 0); 79 - } 80 - 81 - val[3] = buf[1]; 82 - val[2] = buf[2]; 83 - val[1] = buf[3]; 84 - val[0] = buf[4]; 85 - 86 - (u32::from_le_bytes(val), 5) 87 - } 88 - 89 - #[cfg(test)] 90 - mod tests { 91 - #[test] 92 - #[ignore = "this is a very time consuming test, it should be run in release/profiling mode"] 93 - fn pack_all_nums() { 94 - for num in 0..=u32::MAX { 95 - let mut buf = [0u8; 5]; 96 - let rlen = super::pack_dd(num, &mut buf); 97 - assert!(rlen > 0); 98 - 99 - let unpacked = super::unpack_dd(&buf[..rlen]); 100 - assert_eq!(unpacked.1, rlen, "bad unpack size"); 101 - assert_eq!(unpacked.0, num, "values don't match"); 102 - } 103 - } 104 - } 1 + /// packs a dd into `buf` returning amout of bytes written. 2 + /// Returns 0 if buffer is too small 3 + pub fn pack_dd(v: u32, buf: &mut [u8]) -> usize { 4 + let bytes = v.to_le_bytes(); 5 + match v { 6 + 0..=0x7f => { 7 + // 0..0XXXXXXX (7 bits) 8 + if buf.is_empty() { 9 + return 0; 10 + } 11 + buf[0] = bytes[0]; 12 + 1 13 + }, 14 + 0x80..=0x3fff => { 15 + // 10AAAAAA..BBBBBBBB (14 bits) 16 + if buf.len() < 2 { 17 + return 0; 18 + } 19 + buf[0] = 0x80 | bytes[1]; 20 + buf[1] = bytes[0]; 21 + 2 22 + }, 23 + 0x4000..=0x1fffff => { 24 + // 11000000_AAAAAAAA_BBBBBBBB_CCCCCCCC (24 bits) 25 + if buf.len() < 3 { 26 + return 0; 27 + } 28 + buf[0] = 0xc0; 29 + buf[1] = bytes[2]; 30 + buf[2] = bytes[1]; 31 + buf[3] = bytes[0]; 32 + 4 33 + }, 34 + 0x200000..=u32::MAX => { 35 + // 11111111_AAAAAAAA_BBBBBBBB_CCCCCCCC_DDDDDDDD (32 bits) 36 + if buf.len() < 5 { 37 + return 0; 38 + } 39 + buf[0] = 0xff; 40 + buf[1] = bytes[3]; 41 + buf[2] = bytes[2]; 42 + buf[3] = bytes[1]; 43 + buf[4] = bytes[0]; 44 + 5 45 + }, 46 + } 47 + } 48 + 49 + /// unpacks a dd from `buf`, returning the amount (value, byte consumed) 50 + pub fn unpack_dd(buf: &[u8]) -> (u32, usize) { 51 + if buf.is_empty() { 52 + return (0, 0); 53 + } 54 + 55 + let msb = buf[0]; 56 + let mut val = [0u8; 4]; 57 + 58 + if msb & 0x80 == 0 { 59 + // 0...... 60 + val[0] = msb; 61 + return (u32::from_le_bytes(val), 1); 62 + } 63 + if msb & 0x40 == 0 { 64 + // 10....../0x80 65 + if buf.len() < 2 { 66 + return (0, 0); 67 + } 68 + val[1] = msb & 0x3f; 69 + val[0] = buf[1]; 70 + return (u32::from_le_bytes(val), 2); 71 + } 72 + if msb & 0x20 == 0 { 73 + // 110...../0xC0 74 + if buf.len() < 4 { 75 + return (0, 0); 76 + } 77 + val[3] = msb & 0x1f; 78 + val[2] = buf[1]; 79 + val[1] = buf[2]; 80 + val[0] = buf[3]; 81 + return (u32::from_le_bytes(val), 4); 82 + } 83 + 84 + if buf.len() < 5 { 85 + return (0, 0); 86 + } 87 + 88 + val[3] = buf[1]; 89 + val[2] = buf[2]; 90 + val[1] = buf[3]; 91 + val[0] = buf[4]; 92 + 93 + (u32::from_le_bytes(val), 5) 94 + } 95 + 96 + #[cfg(test)] 97 + mod tests { 98 + #[test] 99 + #[ignore = "this is a very time consuming test, it should be run in release/profiling mode"] 100 + fn pack_all_nums() { 101 + for num in 0..=u32::MAX { 102 + let mut buf = [0u8; 5]; 103 + let rlen = super::pack_dd(num, &mut buf); 104 + assert!(rlen > 0); 105 + 106 + let unpacked = super::unpack_dd(&buf[..rlen]); 107 + assert_eq!(unpacked.1, rlen, "bad unpack size"); 108 + assert_eq!(unpacked.0, num, "values don't match"); 109 + } 110 + } 111 + }
+255 -241
common/src/rpc/ser.rs
··· 1 - use std::io::Write; 2 - use serde::{ser, Serialize, ser::Impossible}; 3 - use super::Error; 4 - 5 - struct Serializer<W: Write> { 6 - output: W, 7 - } 8 - 9 - pub fn to_writer<T: Serialize, W: Write>(v: &T, w: W) -> Result<(), Error> { 10 - let mut serializer = Serializer { 11 - output: w, 12 - }; 13 - v.serialize(&mut serializer)?; 14 - Ok(()) 15 - } 16 - 17 - #[allow(dead_code)] 18 - pub fn to_vec<T: Serialize>(v: &T) -> Result<Vec<u8>, Error> { 19 - let mut buf = vec![]; 20 - to_writer(v, &mut buf)?; 21 - Ok(buf) 22 - } 23 - 24 - impl<W: Write> Serializer<W> { 25 - fn pack_dd(&mut self, num: u32) -> Result<(), Error> { 26 - let mut buf = [0u8; 5]; 27 - let bytes = super::packing::pack_dd(num, &mut buf); 28 - self.output.write_all(&buf[..bytes])?; 29 - Ok(()) 30 - } 31 - 32 - fn pack_str(&mut self, s: &str) -> Result<(), Error> { 33 - self.output.write_all(s.as_bytes())?; 34 - self.output.write_all(&[0])?; 35 - Ok(()) 36 - } 37 - 38 - fn pack_bytes(&mut self, b: &[u8]) -> Result<(), Error> { 39 - self.pack_dd(b.len() as u32)?; 40 - self.output.write_all(b)?; 41 - Ok(()) 42 - } 43 - } 44 - 45 - impl<'a, W: Write> ser::Serializer for &'a mut Serializer<W> { 46 - type Ok = (); 47 - type Error = Error; 48 - 49 - type SerializeSeq = Self; 50 - type SerializeTuple = Self; 51 - type SerializeTupleStruct = Impossible<(), Self::Error>; 52 - type SerializeTupleVariant = Impossible<(), Self::Error>; 53 - type SerializeMap = Impossible<(), Self::Error>; 54 - type SerializeStruct = Self; 55 - type SerializeStructVariant = Impossible<(), Self::Error>; 56 - 57 - fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> { 58 - unreachable!() 59 - } 60 - 61 - fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> { 62 - unreachable!() 63 - } 64 - 65 - fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> { 66 - unreachable!() 67 - } 68 - 69 - fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> { 70 - unreachable!() 71 - } 72 - 73 - fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> { 74 - unreachable!() 75 - } 76 - 77 - fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { 78 - self.output.write_all(&[v])?; 79 - Ok(()) 80 - } 81 - 82 - fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> { 83 - unreachable!() 84 - } 85 - 86 - fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { 87 - self.pack_dd(v) 88 - } 89 - 90 - fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { 91 - let high = (v >> 32) & 0xffffffff; 92 - let low = v & 0xffffffff; 93 - self.pack_dd(high as u32)?; 94 - self.pack_dd(low as u32)?; 95 - Ok(()) 96 - } 97 - 98 - fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> { 99 - unreachable!() 100 - } 101 - 102 - fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> { 103 - unreachable!() 104 - } 105 - 106 - fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> { 107 - unreachable!() 108 - } 109 - 110 - fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { 111 - self.pack_str(v) 112 - } 113 - 114 - fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { 115 - self.pack_bytes(v) 116 - } 117 - 118 - fn serialize_none(self) -> Result<Self::Ok, Self::Error> { 119 - unreachable!() 120 - } 121 - 122 - fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> Result<Self::Ok, Self::Error> { 123 - unreachable!() 124 - } 125 - 126 - fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { 127 - // unit contains no information... 128 - Ok(()) 129 - } 130 - 131 - fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> { 132 - unreachable!() 133 - } 134 - 135 - fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str) -> Result<Self::Ok, Self::Error> { 136 - unreachable!() 137 - } 138 - 139 - fn serialize_newtype_struct<T: ?Sized + Serialize>(self, _name: &'static str, _value: &T) -> Result<Self::Ok, Self::Error> { 140 - unreachable!() 141 - } 142 - 143 - fn serialize_newtype_variant<T: ?Sized + Serialize>(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> Result<Self::Ok, Self::Error> { 144 - unreachable!() 145 - } 146 - 147 - fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { 148 - let len = len.unwrap(); 149 - self.pack_dd(len as u32)?; 150 - Ok(self) 151 - } 152 - 153 - fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> { 154 - Ok(self) 155 - } 156 - 157 - fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct, Self::Error> { 158 - unreachable!(); 159 - } 160 - 161 - fn serialize_tuple_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result<Self::SerializeTupleVariant, Self::Error> { 162 - unreachable!() 163 - } 164 - 165 - fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { 166 - unreachable!() 167 - } 168 - 169 - fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct, Self::Error> { 170 - // structs will simply be flattened 171 - Ok(self) 172 - } 173 - 174 - fn serialize_struct_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> Result<Self::SerializeStructVariant, Self::Error> { 175 - unreachable!() 176 - } 177 - } 178 - 179 - impl<'a, W: Write> ser::SerializeSeq for &'a mut Serializer<W> { 180 - type Ok = (); 181 - type Error = Error; 182 - 183 - fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> { 184 - value.serialize(&mut **self) 185 - } 186 - 187 - fn end(self) -> Result<Self::Ok, Self::Error> { 188 - Ok(()) 189 - } 190 - } 191 - 192 - impl<'a, W: Write> ser::SerializeTuple for &'a mut Serializer<W> { 193 - type Ok = (); 194 - type Error = Error; 195 - 196 - fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> { 197 - value.serialize(&mut **self) 198 - } 199 - 200 - fn end(self) -> Result<Self::Ok, Self::Error> { 201 - Ok(()) 202 - } 203 - } 204 - 205 - impl<'a, W: Write> ser::SerializeStruct for &'a mut Serializer<W> { 206 - type Ok = (); 207 - type Error = Error; 208 - 209 - fn serialize_field<T: ?Sized + Serialize>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error> { 210 - // struct names have no meaning 211 - value.serialize(&mut **self) 212 - } 213 - 214 - fn end(self) -> Result<Self::Ok, Self::Error> { 215 - Ok(()) 216 - } 217 - } 218 - 219 - #[cfg(test)] 220 - mod tests { 221 - #[test] 222 - fn ser_hello() { 223 - #[derive(serde::Serialize)] 224 - struct Test<'a> { 225 - arr: [u8; 16], 226 - s: &'a str, 227 - b: &'a [u8], 228 - i: u32, 229 - q: u64, 230 - } 231 - let v = Test { 232 - arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], 233 - s: "somestring", 234 - b: b"bytes", 235 - i: 0x20, 236 - q: 0x20, 237 - }; 238 - let v = super::to_vec(&v).expect("failed to serialize dummy"); 239 - assert_eq!(v, b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10somestring\x00\x05bytes\x20\x00\x20"); 240 - } 241 - } 1 + use super::Error; 2 + use serde::{ser, ser::Impossible, Serialize}; 3 + use std::io::Write; 4 + 5 + struct Serializer<W: Write> { 6 + output: W, 7 + } 8 + 9 + pub fn to_writer<T: Serialize, W: Write>(v: &T, w: W) -> Result<(), Error> { 10 + let mut serializer = Serializer { output: w }; 11 + v.serialize(&mut serializer)?; 12 + Ok(()) 13 + } 14 + 15 + #[allow(dead_code)] 16 + pub fn to_vec<T: Serialize>(v: &T) -> Result<Vec<u8>, Error> { 17 + let mut buf = vec![]; 18 + to_writer(v, &mut buf)?; 19 + Ok(buf) 20 + } 21 + 22 + impl<W: Write> Serializer<W> { 23 + fn pack_dd(&mut self, num: u32) -> Result<(), Error> { 24 + let mut buf = [0u8; 5]; 25 + let bytes = super::packing::pack_dd(num, &mut buf); 26 + self.output.write_all(&buf[..bytes])?; 27 + Ok(()) 28 + } 29 + 30 + fn pack_str(&mut self, s: &str) -> Result<(), Error> { 31 + self.output.write_all(s.as_bytes())?; 32 + self.output.write_all(&[0])?; 33 + Ok(()) 34 + } 35 + 36 + fn pack_bytes(&mut self, b: &[u8]) -> Result<(), Error> { 37 + self.pack_dd(b.len() as u32)?; 38 + self.output.write_all(b)?; 39 + Ok(()) 40 + } 41 + } 42 + 43 + impl<'a, W: Write> ser::Serializer for &'a mut Serializer<W> { 44 + type Ok = (); 45 + type Error = Error; 46 + 47 + type SerializeSeq = Self; 48 + type SerializeTuple = Self; 49 + type SerializeTupleStruct = Impossible<(), Self::Error>; 50 + type SerializeTupleVariant = Impossible<(), Self::Error>; 51 + type SerializeMap = Impossible<(), Self::Error>; 52 + type SerializeStruct = Self; 53 + type SerializeStructVariant = Impossible<(), Self::Error>; 54 + 55 + fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> { 56 + unreachable!() 57 + } 58 + 59 + fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> { 60 + unreachable!() 61 + } 62 + 63 + fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> { 64 + unreachable!() 65 + } 66 + 67 + fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> { 68 + unreachable!() 69 + } 70 + 71 + fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> { 72 + unreachable!() 73 + } 74 + 75 + fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { 76 + self.output.write_all(&[v])?; 77 + Ok(()) 78 + } 79 + 80 + fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> { 81 + unreachable!() 82 + } 83 + 84 + fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { 85 + self.pack_dd(v) 86 + } 87 + 88 + fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { 89 + let high = (v >> 32) & 0xffffffff; 90 + let low = v & 0xffffffff; 91 + self.pack_dd(high as u32)?; 92 + self.pack_dd(low as u32)?; 93 + Ok(()) 94 + } 95 + 96 + fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> { 97 + unreachable!() 98 + } 99 + 100 + fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> { 101 + unreachable!() 102 + } 103 + 104 + fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> { 105 + unreachable!() 106 + } 107 + 108 + fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { 109 + self.pack_str(v) 110 + } 111 + 112 + fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { 113 + self.pack_bytes(v) 114 + } 115 + 116 + fn serialize_none(self) -> Result<Self::Ok, Self::Error> { 117 + unreachable!() 118 + } 119 + 120 + fn serialize_some<T: ?Sized + Serialize>(self, _value: &T) -> Result<Self::Ok, Self::Error> { 121 + unreachable!() 122 + } 123 + 124 + fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { 125 + // unit contains no information... 126 + Ok(()) 127 + } 128 + 129 + fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> { 130 + unreachable!() 131 + } 132 + 133 + fn serialize_unit_variant( 134 + self, _name: &'static str, _variant_index: u32, _variant: &'static str, 135 + ) -> Result<Self::Ok, Self::Error> { 136 + unreachable!() 137 + } 138 + 139 + fn serialize_newtype_struct<T: ?Sized + Serialize>( 140 + self, _name: &'static str, _value: &T, 141 + ) -> Result<Self::Ok, Self::Error> { 142 + unreachable!() 143 + } 144 + 145 + fn serialize_newtype_variant<T: ?Sized + Serialize>( 146 + self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, 147 + ) -> Result<Self::Ok, Self::Error> { 148 + unreachable!() 149 + } 150 + 151 + fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { 152 + let len = len.unwrap(); 153 + self.pack_dd(len as u32)?; 154 + Ok(self) 155 + } 156 + 157 + fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> { 158 + Ok(self) 159 + } 160 + 161 + fn serialize_tuple_struct( 162 + self, _name: &'static str, _len: usize, 163 + ) -> Result<Self::SerializeTupleStruct, Self::Error> { 164 + unreachable!(); 165 + } 166 + 167 + fn serialize_tuple_variant( 168 + self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, 169 + ) -> Result<Self::SerializeTupleVariant, Self::Error> { 170 + unreachable!() 171 + } 172 + 173 + fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { 174 + unreachable!() 175 + } 176 + 177 + fn serialize_struct( 178 + self, _name: &'static str, _len: usize, 179 + ) -> Result<Self::SerializeStruct, Self::Error> { 180 + // structs will simply be flattened 181 + Ok(self) 182 + } 183 + 184 + fn serialize_struct_variant( 185 + self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, 186 + ) -> Result<Self::SerializeStructVariant, Self::Error> { 187 + unreachable!() 188 + } 189 + } 190 + 191 + impl<'a, W: Write> ser::SerializeSeq for &'a mut Serializer<W> { 192 + type Ok = (); 193 + type Error = Error; 194 + 195 + fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> { 196 + value.serialize(&mut **self) 197 + } 198 + 199 + fn end(self) -> Result<Self::Ok, Self::Error> { 200 + Ok(()) 201 + } 202 + } 203 + 204 + impl<'a, W: Write> ser::SerializeTuple for &'a mut Serializer<W> { 205 + type Ok = (); 206 + type Error = Error; 207 + 208 + fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> { 209 + value.serialize(&mut **self) 210 + } 211 + 212 + fn end(self) -> Result<Self::Ok, Self::Error> { 213 + Ok(()) 214 + } 215 + } 216 + 217 + impl<'a, W: Write> ser::SerializeStruct for &'a mut Serializer<W> { 218 + type Ok = (); 219 + type Error = Error; 220 + 221 + fn serialize_field<T: ?Sized + Serialize>( 222 + &mut self, _key: &'static str, value: &T, 223 + ) -> Result<(), Self::Error> { 224 + // struct names have no meaning 225 + value.serialize(&mut **self) 226 + } 227 + 228 + fn end(self) -> Result<Self::Ok, Self::Error> { 229 + Ok(()) 230 + } 231 + } 232 + 233 + #[cfg(test)] 234 + mod tests { 235 + #[test] 236 + fn ser_hello() { 237 + #[derive(serde::Serialize)] 238 + struct Test<'a> { 239 + arr: [u8; 16], 240 + s: &'a str, 241 + b: &'a [u8], 242 + i: u32, 243 + q: u64, 244 + } 245 + let v = Test { 246 + arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], 247 + s: "somestring", 248 + b: b"bytes", 249 + i: 0x20, 250 + q: 0x20, 251 + }; 252 + let v = super::to_vec(&v).expect("failed to serialize dummy"); 253 + assert_eq!(v, b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10somestring\x00\x05bytes\x20\x00\x20"); 254 + } 255 + }
+192 -229
common/src/web/api.rs
··· 1 - use std::borrow::Cow; 2 - use log::*; 3 - use warp::{Filter, Reply, Rejection}; 4 - use crate::{db::DbStats}; 5 - use serde::Serialize; 6 - 7 - use super::SharedState; 8 - 9 - struct Md5([u8; 16]); 10 - impl std::str::FromStr for Md5 { 11 - type Err = &'static str; 12 - fn from_str(s: &str) -> Result<Md5, Self::Err> { 13 - let mut res = [0u8; 16]; 14 - let s = s.trim(); 15 - if s.len() != 32 { 16 - return Err("bad md5 length"); 17 - } 18 - binascii::hex2bin(s.as_bytes(), &mut res) 19 - .map_err(|_| "bad md5")?; 20 - Ok(Md5(res)) 21 - } 22 - } 23 - 24 - #[derive(Serialize)] 25 - struct Error<'a> { 26 - error: &'a str, 27 - } 28 - 29 - impl std::fmt::Display for Md5 { 30 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 31 - let mut out = [0u8; 32]; 32 - binascii::bin2hex(&self.0, &mut out).unwrap(); 33 - let out = std::str::from_utf8(&out).unwrap(); 34 - write!(f, "{}", &out) 35 - } 36 - } 37 - 38 - impl Serialize for Md5 { 39 - fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 40 - serializer.serialize_str(&format!("{}", &self)) 41 - } 42 - } 43 - 44 - pub fn api_root(state: SharedState) -> impl Filter<Extract = (impl Reply + 'static, ), Error=Rejection> + Clone { 45 - let view_file = warp::get() 46 - .and(warp::path("files")) 47 - .and(super::with_state(state.clone())) 48 - .and(warp::filters::path::param::<Md5>()) 49 - .and_then(view_file_by_hash); 50 - let view_func = warp::get() 51 - .and(warp::path("funcs")) 52 - .and(super::with_state(state.clone())) 53 - .and(warp::filters::path::param::<Md5>()) 54 - .and_then(view_func_by_hash); 55 - let view_status = warp::get() 56 - .and(warp::path("status")) 57 - .and(super::with_state(state)) 58 - .and_then(view_status); 59 - 60 - view_file 61 - .or(view_func) 62 - .or(view_status) 63 - } 64 - 65 - // GET server/api/files/:md5 66 - async fn view_file_by_hash(state: SharedState, md5: Md5) -> Result<impl Reply, Rejection> { 67 - #[derive(Serialize)] 68 - struct FileFunc { 69 - hash: Md5, 70 - len: u32, 71 - name: String, 72 - } 73 - 74 - let v = match state.db.get_file_funcs(&md5.0[..], 0, 10_000).await { 75 - Ok(v) => v, 76 - Err(err) => { 77 - error!("failed to get file's funcs {}: {}", &md5, err); 78 - return Ok(warp::reply::json(&Error{error: "internal server error"})); 79 - }, 80 - }; 81 - let v: Vec<_> = v.into_iter() 82 - .map(|v| { 83 - FileFunc { 84 - name: v.0, 85 - len: v.1, 86 - hash: Md5(v.2), 87 - } 88 - }) 89 - .collect(); 90 - 91 - Result::<_, Rejection>::Ok(warp::reply::json(&v)) 92 - } 93 - 94 - // GET server/api/funcs/:md5 95 - async fn view_func_by_hash(state: SharedState, md5: Md5) -> Result<impl Reply, Rejection> { 96 - #[derive(Serialize)] 97 - enum CommentType { 98 - Posterior, 99 - Anterior, 100 - Function{ repeatable: bool }, 101 - Byte { repeatable: bool }, 102 - } 103 - 104 - #[derive(Serialize)] 105 - struct Comment<'a> { 106 - #[serde(skip_serializing_if = "Option::is_none")] 107 - offset: Option<u32>, 108 - #[serde(rename = "type")] 109 - type_: CommentType, 110 - comment: Cow<'a, str>, 111 - } 112 - 113 - #[derive(Serialize)] 114 - struct FuncInfo<'a> { 115 - name: &'a str, 116 - comments: Vec<Comment<'a>>, 117 - length: u32, 118 - in_files: &'a [Md5], 119 - } 120 - 121 - let funcs = [crate::rpc::PullMetadataFunc { 122 - unk0: 1, 123 - mb_hash: &md5.0 124 - }]; 125 - 126 - let files_with = state.db.get_files_with_func(&md5.0[..]); 127 - let files_info = state.db.get_funcs(&funcs); 128 - 129 - let (files_with, files_info) = match futures_util::try_join!(files_with, files_info) { 130 - Ok(v) => v, 131 - Err(err) => { 132 - error!("failed to execute db queries: {}", err); 133 - return Ok(warp::reply::json(&Error {error: "internal server error"})); 134 - } 135 - }; 136 - 137 - let files_with: Vec<Md5> = files_with.into_iter().map(Md5).collect(); 138 - 139 - let v = files_info; 140 - let v: Vec<FuncInfo> = v 141 - .iter() 142 - .take(1) 143 - .filter_map(|v| v.as_ref()) 144 - .filter_map(|v| { 145 - let md = match crate::md::parse_metadata(&v.data) { 146 - Ok(v) => v, 147 - Err(e) => { 148 - error!("error parsing metadata for {}: {}", &md5, e); 149 - return None; 150 - } 151 - }; 152 - let comments: Vec<Comment> = md.into_iter() 153 - .filter_map(|md| { 154 - match md { 155 - crate::md::FunctionMetadata::ByteComment(c) => { 156 - Some(vec![Comment { 157 - offset: Some(c.offset), 158 - type_: CommentType::Byte{ repeatable: c.is_repeatable }, 159 - comment: c.comment.into(), 160 - }]) 161 - }, 162 - crate::md::FunctionMetadata::FunctionComment(c) => { 163 - Some(vec![Comment { 164 - offset: None, 165 - type_: CommentType::Function{ repeatable: c.is_repeatable }, 166 - comment: c.comment.into(), 167 - }]) 168 - }, 169 - crate::md::FunctionMetadata::ExtraComment(c) => { 170 - let mut res = vec![]; 171 - if !c.anterior.is_empty() { 172 - res.push(Comment { 173 - offset: Some(c.offset), 174 - type_: CommentType::Anterior, 175 - comment: c.anterior.into(), 176 - }); 177 - } 178 - if !c.posterior.is_empty() { 179 - res.push(Comment { 180 - offset: Some(c.offset), 181 - type_: CommentType::Posterior, 182 - comment: c.posterior.into(), 183 - }); 184 - } 185 - if !res.is_empty() { 186 - Some(res) 187 - } else { 188 - None 189 - } 190 - }, 191 - } 192 - }) 193 - .flatten() 194 - .collect(); 195 - Some(FuncInfo { 196 - name: &v.name, 197 - length: v.len, 198 - comments, 199 - in_files: &files_with, 200 - }) 201 - }).collect(); 202 - 203 - Result::<_, Rejection>::Ok(warp::reply::json(&v)) 204 - } 205 - 206 - // GET /api/status 207 - async fn view_status(state: SharedState) -> Result<impl Reply, Rejection> { 208 - #[derive(Serialize)] 209 - enum DbStatus { 210 - Stats(DbStats), 211 - Error(String), 212 - } 213 - 214 - #[derive(Serialize)] 215 - struct Response { 216 - db_online: bool, 217 - stats: DbStatus, 218 - } 219 - 220 - let stats = match state.db.get_stats().await { 221 - Ok(stats) => DbStatus::Stats(stats), 222 - Err(err) => DbStatus::Error(format!("{}", err)), 223 - }; 224 - 225 - Result::<_, Rejection>::Ok(warp::reply::json(&Response { 226 - db_online: state.db.is_online().await, 227 - stats, 228 - })) 229 - } 1 + use log::*; 2 + use serde::Serialize; 3 + use std::borrow::Cow; 4 + use warp::{Filter, Rejection, Reply}; 5 + 6 + use super::SharedState; 7 + 8 + struct Md5([u8; 16]); 9 + impl std::str::FromStr for Md5 { 10 + type Err = &'static str; 11 + fn from_str(s: &str) -> Result<Md5, Self::Err> { 12 + let mut res = [0u8; 16]; 13 + let s = s.trim(); 14 + if s.len() != 32 { 15 + return Err("bad md5 length"); 16 + } 17 + binascii::hex2bin(s.as_bytes(), &mut res).map_err(|_| "bad md5")?; 18 + Ok(Md5(res)) 19 + } 20 + } 21 + 22 + #[derive(Serialize)] 23 + struct Error<'a> { 24 + error: &'a str, 25 + } 26 + 27 + impl std::fmt::Display for Md5 { 28 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 + let mut out = [0u8; 32]; 30 + binascii::bin2hex(&self.0, &mut out).unwrap(); 31 + let out = std::str::from_utf8(&out).unwrap(); 32 + write!(f, "{}", &out) 33 + } 34 + } 35 + 36 + impl Serialize for Md5 { 37 + fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 38 + serializer.serialize_str(&format!("{}", &self)) 39 + } 40 + } 41 + 42 + pub fn api_root( 43 + state: SharedState, 44 + ) -> impl Filter<Extract = (impl Reply + 'static,), Error = Rejection> + Clone { 45 + let view_file = warp::get() 46 + .and(warp::path("files")) 47 + .and(super::with_state(state.clone())) 48 + .and(warp::filters::path::param::<Md5>()) 49 + .and_then(view_file_by_hash); 50 + let view_func = warp::get() 51 + .and(warp::path("funcs")) 52 + .and(super::with_state(state)) 53 + .and(warp::filters::path::param::<Md5>()) 54 + .and_then(view_func_by_hash); 55 + 56 + view_file.or(view_func) 57 + } 58 + 59 + // GET server/api/files/:md5 60 + async fn view_file_by_hash(state: SharedState, md5: Md5) -> Result<impl Reply, Rejection> { 61 + #[derive(Serialize)] 62 + struct FileFunc { 63 + hash: Md5, 64 + len: u32, 65 + name: String, 66 + } 67 + 68 + let v = match state.db.get_file_funcs(&md5.0[..], 0, 10_000).await { 69 + Ok(v) => v, 70 + Err(err) => { 71 + error!("failed to get file's funcs {}: {}", &md5, err); 72 + return Ok(warp::reply::json(&Error { error: "internal server error" })); 73 + }, 74 + }; 75 + let v: Vec<_> = v 76 + .into_iter() 77 + .map(|v| { 78 + let mut hash = [0u8; 16]; 79 + hash.copy_from_slice(&v.2); 80 + FileFunc { name: v.0, len: v.1 as u32, hash: Md5(hash) } 81 + }) 82 + .collect(); 83 + 84 + Result::<_, Rejection>::Ok(warp::reply::json(&v)) 85 + } 86 + 87 + // GET server/api/funcs/:md5 88 + async fn view_func_by_hash(state: SharedState, md5: Md5) -> Result<impl Reply, Rejection> { 89 + #[derive(Serialize)] 90 + enum CommentType { 91 + Posterior, 92 + Anterior, 93 + Function { repeatable: bool }, 94 + Byte { repeatable: bool }, 95 + } 96 + 97 + #[derive(Serialize)] 98 + struct Comment<'a> { 99 + #[serde(skip_serializing_if = "Option::is_none")] 100 + offset: Option<u32>, 101 + #[serde(rename = "type")] 102 + type_: CommentType, 103 + comment: Cow<'a, str>, 104 + } 105 + 106 + #[derive(Serialize)] 107 + struct FuncInfo<'a> { 108 + name: &'a str, 109 + comments: Vec<Comment<'a>>, 110 + length: u32, 111 + in_files: &'a [Md5], 112 + } 113 + 114 + let funcs = [crate::rpc::PullMetadataFunc { unk0: 1, mb_hash: &md5.0 }]; 115 + 116 + let files_with = state.db.get_files_with_func(&md5.0[..]); 117 + let files_info = state.db.get_funcs(&funcs); 118 + 119 + let (files_with, files_info) = match futures_util::try_join!(files_with, files_info) { 120 + Ok(v) => v, 121 + Err(err) => { 122 + error!("failed to execute db queries: {}", err); 123 + return Ok(warp::reply::json(&Error { error: "internal server error" })); 124 + }, 125 + }; 126 + 127 + let files_with: Vec<Md5> = files_with 128 + .into_iter() 129 + .map(|v| { 130 + let mut md5 = [0u8; 16]; 131 + md5.copy_from_slice(&v); 132 + Md5(md5) 133 + }) 134 + .collect(); 135 + 136 + let v = files_info; 137 + let v: Vec<FuncInfo> = v 138 + .iter() 139 + .take(1) 140 + .filter_map(|v| v.as_ref()) 141 + .filter_map(|v| { 142 + let md = match crate::md::parse_metadata(&v.data) { 143 + Ok(v) => v, 144 + Err(e) => { 145 + error!("error parsing metadata for {}: {}", &md5, e); 146 + return None; 147 + }, 148 + }; 149 + let comments: Vec<Comment> = md 150 + .into_iter() 151 + .filter_map(|md| match md { 152 + crate::md::FunctionMetadata::ByteComment(c) => Some(vec![Comment { 153 + offset: Some(c.offset), 154 + type_: CommentType::Byte { repeatable: c.is_repeatable }, 155 + comment: c.comment.into(), 156 + }]), 157 + crate::md::FunctionMetadata::FunctionComment(c) => Some(vec![Comment { 158 + offset: None, 159 + type_: CommentType::Function { repeatable: c.is_repeatable }, 160 + comment: c.comment.into(), 161 + }]), 162 + crate::md::FunctionMetadata::ExtraComment(c) => { 163 + let mut res = vec![]; 164 + if !c.anterior.is_empty() { 165 + res.push(Comment { 166 + offset: Some(c.offset), 167 + type_: CommentType::Anterior, 168 + comment: c.anterior.into(), 169 + }); 170 + } 171 + if !c.posterior.is_empty() { 172 + res.push(Comment { 173 + offset: Some(c.offset), 174 + type_: CommentType::Posterior, 175 + comment: c.posterior.into(), 176 + }); 177 + } 178 + if !res.is_empty() { 179 + Some(res) 180 + } else { 181 + None 182 + } 183 + }, 184 + }) 185 + .flatten() 186 + .collect(); 187 + Some(FuncInfo { name: &v.name, length: v.len, comments, in_files: &files_with }) 188 + }) 189 + .collect(); 190 + 191 + Result::<_, Rejection>::Ok(warp::reply::json(&v)) 192 + }
+10 -8
common/src/web.rs
··· 1 - use crate::SharedState; 2 - use warp::Filter; 3 - 4 - pub mod api; 5 - 6 - pub fn with_state(state: SharedState) -> impl Filter<Extract=(SharedState,), Error= std::convert::Infallible> + Clone { 7 - warp::any().map(move || state.clone()) 8 - } 1 + use crate::SharedState; 2 + use warp::Filter; 3 + 4 + pub mod api; 5 + 6 + pub fn with_state( 7 + state: SharedState, 8 + ) -> impl Filter<Extract = (SharedState,), Error = std::convert::Infallible> + Clone { 9 + warp::any().map(move || state.clone()) 10 + }
+6 -1
config-example.toml
··· 6 6 # server display name; appears in IDA output window 7 7 server_name = "lumen" 8 8 9 + # Allow clients to delete metadata from the database? 10 + allow_deletes = false 11 + # How many function histories should we return? 0=Disabled. 12 + get_history_limit = 50 13 + 9 14 # only required when `use_tls` is set to true. 10 15 [lumina.tls] 11 16 # Specify the server's certificate. ··· 15 20 16 21 [database] 17 22 # Specifies a postgresql connection string. All variables can be found here: https://docs.rs/tokio-postgres/0.6.0/tokio_postgres/config/struct.Config.html 18 - connection_info = "host=127.0.0.1 port=49153 user=postgres password=1" 23 + connection_info = "postgres://postgres:1@127.0.0.1/postgres" 19 24 # Sets if the database connection should be made using TLS. 20 25 use_tls = false 21 26 # If the database requires a secure connection, paths to server-ca and client-id certificates can be set here:
+37 -29
docker-compose.yml
··· 1 - version: '3.7' 1 + name: lumen 2 + volumes: 3 + postgres_data: 2 4 3 - volumes: 4 - postgres_data: 5 5 services: 6 - db: 7 - image: postgres:13-alpine 8 - container_name: lumina-postgres 9 - environment: 10 - POSTGRES_USER: lumina 11 - POSTGRES_DB: lumina 12 - POSTGRES_PASSWORD: 1 13 - expose: 14 - - "5432" 15 - volumes: 16 - - postgres_data:/var/lib/postgresql 17 - - ./schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro 18 - lumina: 19 - build: . 20 - command: sh -c 'while ! nc -vv -z db 5432; do sleep 1; done; /lumen/docker-init.sh' 21 - depends_on: 22 - - db 23 - ports: 24 - - 1234:1234 25 - - 8082:8082 26 - environment: 27 - PKCSPASSWD: $PKCSPASSWD 28 - volumes: 29 - - ./dockershare:/dockershare 30 - links: 31 - - db 6 + db: 7 + image: postgres:15.1-alpine 8 + container_name: lumina-postgres 9 + healthcheck: 10 + test: ["CMD", "pg_isready", "-U", "lumina"] 11 + interval: 5s 12 + retries: 10 13 + timeout: 5s 14 + environment: 15 + POSTGRES_USER: lumina 16 + POSTGRES_DB: lumina 17 + POSTGRES_PASSWORD: 1 18 + expose: 19 + - "5432" 20 + volumes: 21 + - postgres_data:/var/lib/postgresql 22 + mem_swappiness: 0 23 + 24 + lumina: 25 + build: . 26 + image: ghcr.io/naim94a/lumen:master 27 + depends_on: 28 + db: 29 + condition: service_healthy 30 + ports: 31 + - 1234:1234 32 + - 8082:8082 33 + environment: 34 + PKCSPASSWD: $PKCSPASSWD 35 + DATABASE_URL: postgres://lumina:1@db/lumina 36 + volumes: 37 + - ./dockershare:/dockershare 38 + links: 39 + - db
+5 -2
docker-init.sh
··· 5 5 echo "Exiting due to error: $@" && exit 1 6 6 } 7 7 do_config_fixup(){ 8 - sed -i -e "s,connection_info.*,connection_info = \"host=db port=5432 user=lumina password=1\"," \ 8 + sed -i -e "s,connection_info.*,connection_info = \"${DATABASE_URL}\"," \ 9 9 /lumen/config.toml 10 10 } 11 11 use_default_config(){ ··· 66 66 67 67 setup_config ; 68 68 do_config_fixup ; 69 - lumen -c /lumen/config.toml || die "Launching lumen"; 69 + echo Running DB migrations... 70 + diesel --config-file /usr/lib/lumen/diesel.toml migration run 71 + echo Migrations done. 72 + exec lumen -c /lumen/config.toml
+8 -7
lumen/Cargo.toml
··· 3 3 description = "lumen server" 4 4 version = "0.2.0" 5 5 authors = ["Naim A. <naim@abda.nl>"] 6 - edition = "2018" 6 + edition = "2021" 7 7 publish = false 8 8 9 9 [dependencies] 10 - common = {path = "../common"} 11 - tokio = {version = "1.21", features = ["full"]} 12 - log = {version = "0.4", features = ["release_max_level_debug"]} 13 - pretty_env_logger = "0.4" 14 - clap = "4.0" 10 + common = { path = "../common" } 11 + tokio = { version = "1.39", features = ["full"] } 12 + log = { version = "0.4", features = ["release_max_level_debug"] } 13 + pretty_env_logger = "0.5" 14 + clap = "4.5" 15 15 tokio-native-tls = "0.3" 16 - native-tls = {version = "0.2"} 16 + native-tls = { version = "0.2" } 17 17 warp = "0.3" 18 + prometheus-client = "0.22"
+11 -355
lumen/src/main.rs
··· 4 4 #![warn(unused_crate_dependencies)] 5 5 #![deny(clippy::all)] 6 6 7 - use common::rpc::{RpcHello, RpcFail}; 8 - use native_tls::Identity; 9 7 use clap::Arg; 10 8 use log::*; 11 - use tokio::time::timeout; 12 - use std::mem::discriminant; 13 - use std::sync::atomic::{AtomicU32, Ordering}; 14 - use std::time::{Duration, Instant}; 15 - use std::{borrow::Cow, sync::Arc}; 16 - use tokio::{net::TcpListener, io::AsyncWrite, io::AsyncRead}; 17 - use std::process::exit; 18 - use common::{SharedState, SharedState_}; 9 + use server::do_lumen; 10 + use std::sync::Arc; 19 11 12 + mod server; 20 13 mod web; 21 14 22 - use common::{config, make_pretty_hex, md, rpc::{self, Error}}; 23 - use common::db::Database; 24 - use rpc::RpcMessage; 15 + use common::config; 25 16 26 17 fn setup_logger() { 27 18 if std::env::var("RUST_LOG").is_err() { ··· 30 21 pretty_env_logger::init_timed(); 31 22 } 32 23 33 - async fn handle_transaction<'a, S: AsyncRead + AsyncWrite + Unpin>(state: &SharedState, user: &'a RpcHello<'a>, mut stream: S) -> Result<(), Error> { 34 - let db = &state.db; 35 - let server_name = state.server_name.as_str(); 36 - 37 - trace!("waiting for command.."); 38 - let req = match timeout(Duration::from_secs(3600), rpc::read_packet(&mut stream)).await { 39 - Ok(res) => match res { 40 - Ok(v) => v, 41 - Err(e) => return Err(e), 42 - }, 43 - Err(_) => { 44 - _ = RpcMessage::Fail(RpcFail { 45 - code: 0, 46 - message: &format!("{server_name} client idle for too long.\n"), 47 - }).async_write(&mut stream).await; 48 - return Err(Error::Timeout); 49 - }, 50 - }; 51 - trace!("got command!"); 52 - let req = match RpcMessage::deserialize(&req) { 53 - Ok(v) => v, 54 - Err(err) => { 55 - warn!("bad message: \n{}\n", make_pretty_hex(&req)); 56 - error!("failed to process rpc message: {}", err); 57 - let resp = rpc::RpcFail{ code: 0, message: &format!("{server_name}: error: invalid data.\n")}; 58 - let resp = RpcMessage::Fail(resp); 59 - resp.async_write(&mut stream).await?; 60 - 61 - return Ok(()); 62 - }, 63 - }; 64 - match req { 65 - RpcMessage::PullMetadata(md) => { 66 - let start = Instant::now(); 67 - let funcs = match timeout(Duration::from_secs(60 * 60), db.get_funcs(&md.funcs)).await { 68 - Ok(r) => match r { 69 - Ok(v) => v, 70 - Err(e) => { 71 - error!("pull failed, db: {}", e); 72 - rpc::RpcMessage::Fail(rpc::RpcFail { 73 - code: 0, 74 - message: &format!("{server_name}: db error; please try again later..\n") 75 - }).async_write(&mut stream).await?; 76 - return Ok(()); 77 - }, 78 - }, 79 - Err(_) => { 80 - RpcMessage::Fail(RpcFail { 81 - code: 0, 82 - message: &format!("{server_name}: query took too long to execute.\n"), 83 - }).async_write(&mut stream).await?; 84 - debug!("pull query timeout"); 85 - return Err(Error::Timeout); 86 - } 87 - }; 88 - debug!("pull {} funcs ended after {:?}", funcs.len(), start.elapsed()); 89 - 90 - let statuses: Vec<u32> = funcs.iter().map(|v| if v.is_none() { 1 } else {0}).collect(); 91 - let found = funcs 92 - .into_iter() 93 - .flatten() 94 - .map(|v| { 95 - rpc::PullMetadataResultFunc { 96 - popularity: v.popularity, 97 - len: v.len, 98 - name: Cow::Owned(v.name), 99 - mb_data: Cow::Owned(v.data), 100 - } 101 - }).collect(); 102 - 103 - RpcMessage::PullMetadataResult(rpc::PullMetadataResult{ 104 - unk0: Cow::Owned(statuses), 105 - funcs: Cow::Owned(found), 106 - }).async_write(&mut stream).await?; 107 - }, 108 - RpcMessage::PushMetadata(mds) => { 109 - // parse the function's metadata 110 - let start = Instant::now(); 111 - let scores: Vec<u32> = mds.funcs.iter() 112 - .map(md::get_score) 113 - .collect(); 114 - 115 - let status = match db.push_funcs(user, &mds, &scores).await { 116 - Ok(v) => { 117 - v.into_iter().map(|v| if v {1} else {0}).collect::<Vec<u32>>() 118 - }, 119 - Err(err) => { 120 - log::error!("push failed, db: {}", err); 121 - rpc::RpcMessage::Fail(rpc::RpcFail { 122 - code: 0, 123 - message: &format!("{server_name}: db error; please try again later.\n") 124 - }).async_write(&mut stream).await?; 125 - return Ok(()); 126 - } 127 - }; 128 - debug!("push {} funcs ended after {:?}", status.len(), start.elapsed()); 129 - 130 - RpcMessage::PushMetadataResult(rpc::PushMetadataResult { 131 - status: Cow::Owned(status), 132 - }).async_write(&mut stream).await?; 133 - }, 134 - _ => { 135 - RpcMessage::Fail(rpc::RpcFail{code: 0, message: &format!("{server_name}: invalid data.\n")}).async_write(&mut stream).await?; 136 - } 137 - } 138 - Ok(()) 139 - } 140 - 141 - async fn handle_client<S: AsyncRead + AsyncWrite + Unpin>(state: &SharedState, mut stream: S) -> Result<(), rpc::Error> { 142 - let server_name = &state.server_name; 143 - let hello = match timeout(Duration::from_secs(15), rpc::read_packet(&mut stream)).await { 144 - Ok(v) => v?, 145 - Err(_) => { 146 - debug!("didn't get hello in time."); 147 - return Ok(()); 148 - }, 149 - }; 150 - 151 - let (hello, creds) = match RpcMessage::deserialize(&hello) { 152 - Ok(RpcMessage::Hello(v, creds)) => { 153 - debug!("hello protocol={}, login creds: {creds:?}", v.protocol_version); 154 - (v, creds) 155 - }, 156 - _ => { 157 - // send error 158 - error!("got bad hello message"); 159 - 160 - let resp = rpc::RpcFail{ code: 0, message: &format!("{}: bad sequence.\n", server_name) }; 161 - let resp = rpc::RpcMessage::Fail(resp); 162 - resp.async_write(&mut stream).await?; 163 - 164 - return Ok(()); 165 - } 166 - }; 167 - 168 - if let Some(ref creds) = creds { 169 - if creds.username != "guest" { 170 - // Only allow "guest" to connect for now. 171 - rpc::RpcMessage::Fail(rpc::RpcFail { 172 - code: 1, 173 - message: &format!("{server_name}: invalid username or password. Try logging in with `guest` instead."), 174 - }).async_write(&mut stream).await?; 175 - return Ok(()); 176 - } 177 - } 178 - 179 - let resp = rpc::RpcMessage::Ok(()); 180 - resp.async_write(&mut stream).await?; 181 - 182 - loop { 183 - handle_transaction(state, &hello, &mut stream).await?; 184 - } 185 - } 186 - 187 - async fn handle_connection<S: AsyncRead + AsyncWrite + Unpin>(state: &SharedState, s: S) { 188 - if let Err(err) = handle_client(state, s).await { 189 - if discriminant(&err) != discriminant(&Error::Eof) { 190 - warn!("err: {}", err); 191 - } 192 - } 193 - } 194 - 195 - async fn serve(listener: TcpListener, accpt: Option<tokio_native_tls::TlsAcceptor>, state: SharedState) { 196 - static COUNTER: AtomicU32 = AtomicU32::new(0); 197 - let accpt = accpt.map(Arc::new); 198 - 199 - loop { 200 - let (client, addr) = match listener.accept().await { 201 - Ok(v) => v, 202 - Err(err) => { 203 - warn!("failed to accept(): {}", err); 204 - continue; 205 - } 206 - }; 207 - let start = Instant::now(); 208 - 209 - let state = state.clone(); 210 - let accpt = accpt.clone(); 211 - tokio::spawn(async move { 212 - let count = { 213 - COUNTER.fetch_add(1, Ordering::Relaxed) + 1 214 - }; 215 - let protocol = if accpt.is_some() {" [TLS]"} else {""}; 216 - debug!("Connection from {:?}{}: {} active connections", &addr, protocol, count); 217 - match accpt { 218 - Some(accpt) => { 219 - match timeout(Duration::from_secs(10), accpt.accept(client)).await { 220 - Ok(r) => match r { 221 - Ok(s) => { 222 - handle_connection(&state, s).await; 223 - }, 224 - Err(err) => debug!("tls accept ({}): {}", &addr, err), 225 - }, 226 - Err(_) => { 227 - debug!("client {} didn't complete ssl handshake in time.", &addr); 228 - }, 229 - }; 230 - }, 231 - None => handle_connection(&state, client).await, 232 - } 233 - 234 - let count = { 235 - COUNTER.fetch_sub(1, Ordering::Relaxed) - 1 236 - }; 237 - debug!("connection with {:?} ended after {:?}; {} active connections", addr, start.elapsed(), count); 238 - }); 239 - } 240 - } 241 - 242 - async fn maintenance(state: std::sync::Weak<SharedState_>) { 243 - let mut timer = tokio::time::interval(std::time::Duration::from_secs(10)); 244 - 245 - loop { 246 - timer.tick().await; 247 - 248 - if let Some(state) = state.upgrade() { 249 - 250 - if !state.db.is_online().await { 251 - warn!("db is offline; attempting to reconnect..."); 252 - match state.db.reconnect().await { 253 - Ok(_) => info!("reconnected."), 254 - Err(err) => error!("failed to reconnect: {}", err), 255 - } 256 - } 257 - 258 - } else { 259 - warn!("shared state is not available"); 260 - break; 261 - } 262 - } 263 - } 264 - 265 - fn main() { 24 + #[tokio::main] 25 + async fn main() { 266 26 setup_logger(); 267 27 let matches = clap::Command::new("lumen") 268 28 .version(env!("CARGO_PKG_VERSION")) ··· 271 31 .arg( 272 32 Arg::new("config") 273 33 .short('c') 274 - .required(true) 275 34 .default_value("config.toml") 276 35 .help("Configuration file path") 277 36 ) 278 37 .get_matches(); 279 38 280 39 let config = { 281 - config::load_config(std::fs::File::open(matches.get_one::<String>("config").unwrap()).expect("failed to read config")) 40 + config::load_config( 41 + std::fs::File::open(matches.get_one::<String>("config").unwrap()) 42 + .expect("failed to read config"), 43 + ) 282 44 }; 283 45 let config = Arc::new(config); 284 46 285 - info!("starting private lumen server..."); 286 - 287 - let rt = match tokio::runtime::Builder::new_multi_thread() 288 - .enable_all() 289 - .thread_stack_size(8 * 1024) 290 - .build() { 291 - Ok(v) => v, 292 - Err(err) => { 293 - error!("failed to create tokio runtime: {}", err); 294 - exit(1); 295 - }, 296 - }; 297 - 298 - let db = rt.block_on(async { 299 - match Database::open(config.clone()).await { 300 - Ok(v) => v, 301 - Err(err) => { 302 - error!("failed to open database: {}", err); 303 - exit(1); 304 - } 305 - } 306 - }); 307 - 308 - let server_name = config.lumina.server_name.clone().unwrap_or_else(|| String::from("lumen")); 309 - 310 - let state = Arc::new(SharedState_{ 311 - db, 312 - config, 313 - server_name, 314 - }); 315 - 316 - rt.spawn(maintenance(Arc::downgrade(&state))); 317 - 318 - let tls_acceptor; 319 - 320 - if state.config.lumina.use_tls.unwrap_or_default() { 321 - let cert_path = &state.config.lumina.tls.as_ref().expect("tls section is missing").server_cert; 322 - let mut crt = match std::fs::read(cert_path) { 323 - Ok(v) => v, 324 - Err(err) => { 325 - error!("failed to read certificate file: {}", err); 326 - exit(1); 327 - } 328 - }; 329 - let pkcs_passwd = std::env::var("PKCSPASSWD").unwrap_or_default(); 330 - let id = match Identity::from_pkcs12(&crt, &pkcs_passwd) { 331 - Ok(v) => v, 332 - Err(err) => { 333 - error!("failed to parse tls certificate: {}", err); 334 - exit(1); 335 - } 336 - }; 337 - let _ = pkcs_passwd; 338 - crt.iter_mut().for_each(|v| *v = 0); 339 - let _ = crt; 340 - let mut accpt = native_tls::TlsAcceptor::builder(id); 341 - accpt.min_protocol_version(Some(native_tls::Protocol::Sslv3)); 342 - let accpt = match accpt.build() { 343 - Ok(v) => v, 344 - Err(err) => { 345 - error!("failed to build tls acceptor: {}", err); 346 - exit(1); 347 - }, 348 - }; 349 - let accpt = tokio_native_tls::TlsAcceptor::from(accpt); 350 - tls_acceptor = Some(accpt); 351 - } else { 352 - tls_acceptor = None; 353 - } 354 - 355 - if let Some(ref webcfg) = state.config.api_server { 356 - let bind_addr = webcfg.bind_addr; 357 - let state = state.clone(); 358 - info!("starting http api server on {:?}", &bind_addr); 359 - rt.spawn(async move { 360 - web::start_webserver(bind_addr, state).await; 361 - }); 362 - } 363 - 364 - let async_server = async { 365 - let server = match TcpListener::bind(state.config.lumina.bind_addr).await { 366 - Ok(v) => v, 367 - Err(err) => { 368 - error!("failed to bind server port: {}", err); 369 - exit(1); 370 - }, 371 - }; 372 - 373 - info!("listening on {:?} secure={}", server.local_addr().unwrap(), tls_acceptor.is_some()); 374 - 375 - serve(server, tls_acceptor, state.clone()).await; 376 - }; 377 - 378 - let ctrlc = tokio::signal::ctrl_c(); 379 - 380 - let racey = async move { 381 - tokio::select! { 382 - _ = async_server => { 383 - error!("server decided to quit. this is impossible."); 384 - }, 385 - _ = ctrlc => { 386 - info!("process was signaled. Shutting down..."); 387 - }, 388 - } 389 - }; 390 - 391 - rt.block_on(racey); 47 + do_lumen(config).await; 392 48 }
+513
lumen/src/server.rs
··· 1 + use std::{ 2 + borrow::Cow, collections::HashMap, mem::discriminant, process::exit, sync::Arc, time::Instant, 3 + }; 4 + 5 + use common::{ 6 + async_drop::AsyncDropper, 7 + config::Config, 8 + db::Database, 9 + make_pretty_hex, md, 10 + metrics::LuminaVersion, 11 + rpc::{self, Error, HelloResult, RpcFail, RpcHello, RpcMessage}, 12 + SharedState, SharedState_, 13 + }; 14 + use log::{debug, error, info, trace, warn}; 15 + use native_tls::Identity; 16 + use tokio::{ 17 + io::{AsyncRead, AsyncWrite}, 18 + net::TcpListener, 19 + time::timeout, 20 + }; 21 + 22 + use crate::web; 23 + 24 + async fn handle_transaction<'a, S: AsyncRead + AsyncWrite + Unpin>( 25 + state: &SharedState, user: &'a RpcHello<'a>, mut stream: S, 26 + ) -> Result<(), Error> { 27 + let db = &state.db; 28 + let server_name = state.server_name.as_str(); 29 + 30 + trace!("waiting for command.."); 31 + let req = 32 + match timeout(state.config.limits.command_timeout, rpc::read_packet(&mut stream)).await { 33 + Ok(res) => match res { 34 + Ok(v) => v, 35 + Err(e) => return Err(e), 36 + }, 37 + Err(_) => { 38 + _ = RpcMessage::Fail(RpcFail { 39 + code: 0, 40 + message: &format!("{server_name} client idle for too long.\n"), 41 + }) 42 + .async_write(&mut stream) 43 + .await; 44 + return Err(Error::Timeout); 45 + }, 46 + }; 47 + trace!("got command!"); 48 + let req = match RpcMessage::deserialize(&req) { 49 + Ok(v) => v, 50 + Err(err) => { 51 + warn!("bad message: \n{}\n", make_pretty_hex(&req)); 52 + error!("failed to process rpc message: {}", err); 53 + let resp = rpc::RpcFail { 54 + code: 0, 55 + message: &format!("{server_name}: error: invalid data.\n"), 56 + }; 57 + let resp = RpcMessage::Fail(resp); 58 + resp.async_write(&mut stream).await?; 59 + 60 + return Ok(()); 61 + }, 62 + }; 63 + match req { 64 + RpcMessage::PullMetadata(md) => { 65 + let start = Instant::now(); 66 + let funcs = 67 + match timeout(state.config.limits.pull_md_timeout, db.get_funcs(&md.funcs)).await { 68 + Ok(r) => match r { 69 + Ok(v) => v, 70 + Err(e) => { 71 + error!("pull failed, db: {}", e); 72 + rpc::RpcMessage::Fail(rpc::RpcFail { 73 + code: 0, 74 + message: &format!( 75 + "{server_name}: db error; please try again later..\n" 76 + ), 77 + }) 78 + .async_write(&mut stream) 79 + .await?; 80 + return Ok(()); 81 + }, 82 + }, 83 + Err(_) => { 84 + RpcMessage::Fail(RpcFail { 85 + code: 0, 86 + message: &format!("{server_name}: query took too long to execute.\n"), 87 + }) 88 + .async_write(&mut stream) 89 + .await?; 90 + debug!("pull query timeout"); 91 + return Err(Error::Timeout); 92 + }, 93 + }; 94 + let pulled_funcs = funcs.iter().filter(|v| v.is_some()).count(); 95 + state.metrics.pulls.inc_by(pulled_funcs as _); 96 + state.metrics.queried_funcs.inc_by(md.funcs.len() as _); 97 + debug!( 98 + "pull {pulled_funcs}/{} funcs ended after {:?}", 99 + md.funcs.len(), 100 + start.elapsed() 101 + ); 102 + 103 + let statuses: Vec<u32> = funcs.iter().map(|v| u32::from(v.is_none())).collect(); 104 + let found = funcs 105 + .into_iter() 106 + .flatten() 107 + .map(|v| rpc::PullMetadataResultFunc { 108 + popularity: v.popularity, 109 + len: v.len, 110 + name: Cow::Owned(v.name), 111 + mb_data: Cow::Owned(v.data), 112 + }) 113 + .collect(); 114 + 115 + RpcMessage::PullMetadataResult(rpc::PullMetadataResult { 116 + unk0: Cow::Owned(statuses), 117 + funcs: Cow::Owned(found), 118 + }) 119 + .async_write(&mut stream) 120 + .await?; 121 + }, 122 + RpcMessage::PushMetadata(mds) => { 123 + // parse the function's metadata 124 + let start = Instant::now(); 125 + let scores: Vec<u32> = mds.funcs.iter().map(md::get_score).collect(); 126 + 127 + let status = match db.push_funcs(user, &mds, &scores).await { 128 + Ok(v) => v.into_iter().map(u32::from).collect::<Vec<u32>>(), 129 + Err(err) => { 130 + log::error!("push failed, db: {}", err); 131 + rpc::RpcMessage::Fail(rpc::RpcFail { 132 + code: 0, 133 + message: &format!("{server_name}: db error; please try again later.\n"), 134 + }) 135 + .async_write(&mut stream) 136 + .await?; 137 + return Ok(()); 138 + }, 139 + }; 140 + state.metrics.pushes.inc_by(status.len() as _); 141 + let new_funcs = 142 + status.iter().fold(0u64, |counter, &v| if v > 0 { counter + 1 } else { counter }); 143 + state.metrics.new_funcs.inc_by(new_funcs); 144 + debug!( 145 + "push {} funcs ended after {:?} ({new_funcs} new)", 146 + status.len(), 147 + start.elapsed() 148 + ); 149 + 150 + RpcMessage::PushMetadataResult(rpc::PushMetadataResult { status: Cow::Owned(status) }) 151 + .async_write(&mut stream) 152 + .await?; 153 + }, 154 + RpcMessage::DelHistory(req) => { 155 + let is_delete_allowed = state.config.lumina.allow_deletes.unwrap_or(false); 156 + if !is_delete_allowed { 157 + RpcMessage::Fail(rpc::RpcFail { 158 + code: 2, 159 + message: &format!("{server_name}: Delete command is disabled on this server."), 160 + }) 161 + .async_write(&mut stream) 162 + .await?; 163 + } else { 164 + if let Err(err) = db.delete_metadata(&req).await { 165 + error!("delete failed. db: {err}"); 166 + RpcMessage::Fail(rpc::RpcFail { 167 + code: 3, 168 + message: &format!("{server_name}: db error, please try again later."), 169 + }) 170 + .async_write(&mut stream) 171 + .await?; 172 + return Ok(()); 173 + } 174 + RpcMessage::DelHistoryResult(rpc::DelHistoryResult { 175 + deleted_mds: req.funcs.len() as u32, 176 + }) 177 + .async_write(&mut stream) 178 + .await?; 179 + } 180 + }, 181 + RpcMessage::GetFuncHistories(req) => { 182 + let limit = state.config.lumina.get_history_limit.unwrap_or(0); 183 + 184 + if limit == 0 { 185 + RpcMessage::Fail(rpc::RpcFail { 186 + code: 4, 187 + message: &format!( 188 + "{server_name}: function histories are disabled on this server." 189 + ), 190 + }) 191 + .async_write(&mut stream) 192 + .await?; 193 + return Ok(()); 194 + } 195 + 196 + let mut statuses = vec![]; 197 + let mut res = vec![]; 198 + for chksum in req.funcs.iter().map(|v| v.mb_hash) { 199 + let history = match db.get_func_histories(chksum, limit).await { 200 + Ok(v) => v, 201 + Err(err) => { 202 + error!("failed to get function histories: {err:?}"); 203 + RpcMessage::Fail(rpc::RpcFail { 204 + code: 3, 205 + message: &format!("{server_name}: db error, please try again later."), 206 + }) 207 + .async_write(&mut stream) 208 + .await?; 209 + return Ok(()); 210 + }, 211 + }; 212 + let status = !history.is_empty() as u32; 213 + statuses.push(status); 214 + if history.is_empty() { 215 + continue; 216 + } 217 + let log = history 218 + .into_iter() 219 + .map(|(updated, name, metadata)| rpc::FunctionHistory { 220 + unk0: 0, 221 + unk1: 0, 222 + name: Cow::Owned(name), 223 + metadata: Cow::Owned(metadata), 224 + timestamp: updated.unix_timestamp() as u64, 225 + author_idx: 0, 226 + idb_path_idx: 0, 227 + }) 228 + .collect::<Vec<_>>(); 229 + res.push(rpc::FunctionHistories { log: Cow::Owned(log) }); 230 + } 231 + 232 + trace!("returning {} histories", res.len()); 233 + 234 + RpcMessage::GetFuncHistoriesResult(rpc::GetFuncHistoriesResult { 235 + status: statuses.into(), 236 + funcs: Cow::Owned(res), 237 + users: vec![].into(), 238 + dbs: vec![].into(), 239 + }) 240 + .async_write(&mut stream) 241 + .await?; 242 + }, 243 + _ => { 244 + RpcMessage::Fail(rpc::RpcFail { 245 + code: 0, 246 + message: &format!("{server_name}: invalid data.\n"), 247 + }) 248 + .async_write(&mut stream) 249 + .await?; 250 + }, 251 + } 252 + Ok(()) 253 + } 254 + 255 + async fn handle_client<S: AsyncRead + AsyncWrite + Unpin>( 256 + state: &SharedState, mut stream: S, 257 + ) -> Result<(), rpc::Error> { 258 + let server_name = &state.server_name; 259 + let hello = 260 + match timeout(state.config.limits.hello_timeout, rpc::read_packet(&mut stream)).await { 261 + Ok(v) => v?, 262 + Err(_) => { 263 + debug!("didn't get hello in time."); 264 + return Ok(()); 265 + }, 266 + }; 267 + 268 + let (hello, creds) = match RpcMessage::deserialize(&hello) { 269 + Ok(RpcMessage::Hello(v, creds)) => { 270 + debug!("hello protocol={}, login creds: {creds:?}", v.protocol_version); 271 + (v, creds) 272 + }, 273 + _ => { 274 + // send error 275 + error!("got bad hello message"); 276 + 277 + let resp = rpc::RpcFail { code: 0, message: &format!("{server_name}: bad sequence.") }; 278 + let resp = rpc::RpcMessage::Fail(resp); 279 + resp.async_write(&mut stream).await?; 280 + 281 + return Ok(()); 282 + }, 283 + }; 284 + state 285 + .metrics 286 + .lumina_version 287 + .get_or_create(&LuminaVersion { protocol_version: hello.protocol_version }) 288 + .inc(); 289 + 290 + if let Some(ref creds) = creds { 291 + if creds.username != "guest" { 292 + // Only allow "guest" to connect for now. 293 + rpc::RpcMessage::Fail(rpc::RpcFail { 294 + code: 1, 295 + message: &format!("{server_name}: invalid username or password. Try logging in with `guest` instead."), 296 + }).async_write(&mut stream).await?; 297 + return Ok(()); 298 + } 299 + } 300 + 301 + let resp = match hello.protocol_version { 302 + 0..=4 => rpc::RpcMessage::Ok(()), 303 + 304 + // starting IDA 8.3 305 + 5.. => { 306 + let mut features = 0; 307 + 308 + if state.config.lumina.allow_deletes.unwrap_or(false) { 309 + features |= 0x02; 310 + } 311 + 312 + rpc::RpcMessage::HelloResult(HelloResult { features, ..Default::default() }) 313 + }, 314 + }; 315 + resp.async_write(&mut stream).await?; 316 + 317 + loop { 318 + handle_transaction(state, &hello, &mut stream).await?; 319 + } 320 + } 321 + 322 + async fn handle_connection<S: AsyncRead + AsyncWrite + Unpin>(state: &SharedState, s: S) { 323 + if let Err(err) = handle_client(state, s).await { 324 + if discriminant(&err) != discriminant(&Error::Eof) { 325 + warn!("err: {}", err); 326 + } 327 + } 328 + } 329 + 330 + async fn serve( 331 + listener: TcpListener, accpt: Option<tokio_native_tls::TlsAcceptor>, state: SharedState, 332 + mut shutdown_signal: tokio::sync::oneshot::Receiver<()>, 333 + ) { 334 + let accpt = accpt.map(Arc::new); 335 + 336 + let (async_drop, worker) = AsyncDropper::new(); 337 + tokio::task::spawn(worker); 338 + 339 + let connections = Arc::new(tokio::sync::Mutex::new(HashMap::< 340 + std::net::SocketAddr, 341 + tokio::task::JoinHandle<()>, 342 + >::new())); 343 + 344 + loop { 345 + let (client, addr) = tokio::select! { 346 + _ = &mut shutdown_signal => { 347 + drop(state); 348 + info!("shutting down..."); 349 + let m = connections.lock().await; 350 + m.iter().for_each(|(k, v)| { 351 + debug!("aborting task for {k}..."); 352 + v.abort(); 353 + }); 354 + return; 355 + }, 356 + res = listener.accept() => match res { 357 + Ok(v) => v, 358 + Err(err) => { 359 + warn!("failed to accept(): {}", err); 360 + continue; 361 + } 362 + }, 363 + }; 364 + 365 + let start = Instant::now(); 366 + 367 + let state = state.clone(); 368 + let accpt = accpt.clone(); 369 + 370 + let conns2 = connections.clone(); 371 + let counter = state.metrics.active_connections.clone(); 372 + let guard = async_drop.defer(async move { 373 + let count = counter.dec() - 1; 374 + debug!( 375 + "connection with {:?} ended after {:?}; {} active connections", 376 + addr, 377 + start.elapsed(), 378 + count 379 + ); 380 + 381 + let mut guard = conns2.lock().await; 382 + if guard.remove(&addr).is_none() { 383 + error!("Couldn't remove connection from set {addr}"); 384 + } 385 + }); 386 + 387 + let counter = state.metrics.active_connections.clone(); 388 + let handle = tokio::spawn(async move { 389 + let _guard = guard; 390 + let count = { counter.inc() + 1 }; 391 + let protocol = if accpt.is_some() { " [TLS]" } else { "" }; 392 + debug!("Connection from {:?}{}: {} active connections", &addr, protocol, count); 393 + match accpt { 394 + Some(accpt) => { 395 + match timeout(state.config.limits.tls_handshake_timeout, accpt.accept(client)) 396 + .await 397 + { 398 + Ok(r) => match r { 399 + Ok(s) => { 400 + handle_connection(&state, s).await; 401 + }, 402 + Err(err) => debug!("tls accept ({}): {}", &addr, err), 403 + }, 404 + Err(_) => { 405 + debug!("client {} didn't complete ssl handshake in time.", &addr); 406 + }, 407 + }; 408 + }, 409 + None => handle_connection(&state, client).await, 410 + } 411 + }); 412 + 413 + let mut guard = connections.lock().await; 414 + guard.insert(addr, handle); 415 + } 416 + } 417 + 418 + pub(crate) async fn do_lumen(config: Arc<Config>) { 419 + info!("starting private lumen server..."); 420 + 421 + let db = match Database::open(&config.database).await { 422 + Ok(v) => v, 423 + Err(err) => { 424 + error!("failed to open database: {}", err); 425 + exit(1); 426 + }, 427 + }; 428 + 429 + let server_name = config.lumina.server_name.clone().unwrap_or_else(|| String::from("lumen")); 430 + 431 + let state = Arc::new(SharedState_ { 432 + db, 433 + config, 434 + server_name, 435 + metrics: common::metrics::Metrics::default(), 436 + }); 437 + 438 + let tls_acceptor; 439 + 440 + if state.config.lumina.use_tls.unwrap_or_default() { 441 + let cert_path = 442 + &state.config.lumina.tls.as_ref().expect("tls section is missing").server_cert; 443 + let mut crt = match std::fs::read(cert_path) { 444 + Ok(v) => v, 445 + Err(err) => { 446 + error!("failed to read certificate file: {}", err); 447 + exit(1); 448 + }, 449 + }; 450 + let pkcs_passwd = std::env::var("PKCSPASSWD").unwrap_or_default(); 451 + let id = match Identity::from_pkcs12(&crt, &pkcs_passwd) { 452 + Ok(v) => v, 453 + Err(err) => { 454 + error!("failed to parse tls certificate: {}", err); 455 + exit(1); 456 + }, 457 + }; 458 + let _ = pkcs_passwd; 459 + crt.iter_mut().for_each(|v| *v = 0); 460 + let _ = crt; 461 + let mut accpt = native_tls::TlsAcceptor::builder(id); 462 + accpt.min_protocol_version(Some(native_tls::Protocol::Sslv3)); 463 + let accpt = match accpt.build() { 464 + Ok(v) => v, 465 + Err(err) => { 466 + error!("failed to build tls acceptor: {}", err); 467 + exit(1); 468 + }, 469 + }; 470 + let accpt = tokio_native_tls::TlsAcceptor::from(accpt); 471 + tls_acceptor = Some(accpt); 472 + } else { 473 + tls_acceptor = None; 474 + } 475 + 476 + let web_handle = if let Some(ref webcfg) = state.config.api_server { 477 + let bind_addr = webcfg.bind_addr; 478 + let state = state.clone(); 479 + info!("starting http api server on {:?}", &bind_addr); 480 + Some(tokio::spawn(async move { 481 + web::start_webserver(bind_addr, state).await; 482 + })) 483 + } else { 484 + None 485 + }; 486 + 487 + let (exit_signal_tx, exit_signal_rx) = tokio::sync::oneshot::channel::<()>(); 488 + 489 + let async_server = async move { 490 + let server = match TcpListener::bind(state.config.lumina.bind_addr).await { 491 + Ok(v) => v, 492 + Err(err) => { 493 + error!("failed to bind server port: {}", err); 494 + exit(1); 495 + }, 496 + }; 497 + 498 + info!("listening on {:?} secure={}", server.local_addr().unwrap(), tls_acceptor.is_some()); 499 + 500 + serve(server, tls_acceptor, state, exit_signal_rx).await; 501 + }; 502 + 503 + let server_handle = tokio::task::spawn(async_server); 504 + tokio::signal::ctrl_c().await.unwrap(); 505 + debug!("CTRL-C; exiting..."); 506 + if let Some(handle) = web_handle { 507 + handle.abort(); 508 + } 509 + exit_signal_tx.send(()).unwrap(); 510 + server_handle.await.unwrap(); 511 + 512 + info!("Goodbye."); 513 + }
+33 -19
lumen/src/web.rs
··· 1 - use std::net::SocketAddr; 2 - 3 - use warp::{self, Filter}; 4 - use common::{SharedState, web::api::api_root}; 5 - 6 - pub async fn start_webserver<A: Into<SocketAddr> + 'static>(bind_addr: A, shared_state: SharedState) { 7 - let root = warp::get() 8 - .and(warp::path::end()) 9 - .map(|| warp::reply::html(include_str!("home.html"))); 10 - 11 - let api = warp::path("api") 12 - .and(api_root(shared_state)); 13 - 14 - let routes = root 15 - .or(api); 16 - 17 - warp::serve(routes) 18 - .run(bind_addr).await; 19 - } 1 + use std::net::SocketAddr; 2 + 3 + use common::{web::api::api_root, SharedState}; 4 + use log::error; 5 + use warp::{hyper::StatusCode, reply::Response, Filter}; 6 + 7 + pub async fn start_webserver<A: Into<SocketAddr> + 'static>( 8 + bind_addr: A, shared_state: SharedState, 9 + ) { 10 + let root = 11 + warp::get().and(warp::path::end()).map(|| warp::reply::html(include_str!("home.html"))); 12 + 13 + let shared_state1 = shared_state.clone(); 14 + let api = warp::path("api").and(api_root(shared_state1)); 15 + 16 + let metrics = warp::get().and(warp::path("metrics")).and(warp::path::end()).map(move || { 17 + let mut res = String::new(); 18 + if let Err(err) = 19 + prometheus_client::encoding::text::encode(&mut res, &shared_state.metrics.registry) 20 + { 21 + error!("failed to encode metrics: {err}"); 22 + let mut r = Response::default(); 23 + *r.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; 24 + r 25 + } else { 26 + warp::reply::Response::new(res.into()) 27 + } 28 + }); 29 + 30 + let routes = root.or(api).or(metrics); 31 + 32 + warp::serve(routes).run(bind_addr).await; 33 + }
+5
rustfmt.toml
··· 1 + edition = "2021" 2 + fn_params_layout = "Compressed" 3 + match_block_trailing_comma = true 4 + newline_style = "Unix" 5 + use_small_heuristics = "Max"
-39
schema.sql
··· 1 - CREATE TABLE users ( 2 - id SERIAL PRIMARY KEY, 3 - lic_id bytea, 4 - lic_data bytea, 5 - hostname VARCHAR(260), 6 - first_seen TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP 7 - ); 8 - CREATE UNIQUE INDEX user_rec ON users(lic_id,lic_data,hostname); 9 - CREATE UNIQUE INDEX user_hn_null ON users (lic_id,lic_data, (hostname IS NULL)) WHERE hostname is NULL; 10 - 11 - CREATE TABLE files ( 12 - id SERIAL PRIMARY KEY, 13 - chksum bytea UNIQUE /* file chksum */ 14 - ); 15 - 16 - CREATE TABLE dbs ( 17 - id SERIAL PRIMARY KEY, 18 - file_path VARCHAR(260), 19 - idb_path VARCHAR(260), 20 - file_id INTEGER REFERENCES files(id), 21 - user_id INTEGER REFERENCES users(id) 22 - ); 23 - CREATE UNIQUE INDEX db_paths ON dbs(file_id, user_id, idb_path); 24 - 25 - CREATE TABLE funcs ( 26 - id SERIAL PRIMARY KEY, 27 - name TEXT NOT NULL, 28 - len INTEGER NOT NULL, 29 - db_id INTEGER REFERENCES dbs(id) NOT NULL, 30 - chksum bytea, /* function chksum */ 31 - metadata bytea, 32 - rank INTEGER, 33 - 34 - push_dt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 35 - update_dt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP 36 - ); 37 - CREATE UNIQUE INDEX funcs_db ON funcs(chksum, db_id); 38 - CREATE INDEX funcs_ranking ON funcs(chksum,rank); 39 - CREATE INDEX func_chksum ON funcs(chksum);