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 toolchain: stable 36 override: true 37 profile: minimal 38 - name: Build 39 run: cargo build 40 - name: Run tests
··· 35 toolchain: stable 36 override: true 37 profile: minimal 38 + - name: Code Formatting 39 + run: cargo fmt --check 40 - name: Build 41 run: cargo build 42 - name: Run tests
+59
CHANGELOG.md
···
··· 1 + # Changelog 2 + 3 + ## [Unreleased] - _TBD_ 4 + 5 + ### Added 6 + 7 + - User management 8 + 9 + ## [v0.4.0] - 2024-03-19 10 + 11 + ### Added 12 + 13 + - Implemented the function histories command. 14 + - Configurable time limits. 15 + 16 + ### Fixed 17 + 18 + - RPC HelloResult will now report if deletes are enabled. 19 + 20 + ### Changes 21 + 22 + - Applied code formatting using `cargo fmt` 23 + 24 + ## [v0.3.0] - 2023-08-22 25 + 26 + ### Added 27 + 28 + - This changelog. 29 + - Support for IDA 8.1+ delete command. 30 + - Pooling for connections to database. 31 + - Attempt to cancel immutable database queries if client leaves. 32 + - Database migrations via Diesel ORM. 33 + - Support for IDA 8.3+ hello response. 34 + - Add Metrics for prometheus. 35 + 36 + ### Fixed 37 + 38 + - 8K stack size is too small for debug builds. 39 + 40 + ## [v0.2.0] - 2022-10-12 41 + 42 + ### Added 43 + 44 + - Protocol: support for IDA 8.1+ user authentication. 45 + - Client connection duration limitations. 46 + 47 + ### Changed 48 + 49 + - Tokio's thread size is reduced from 4M to 8K. 50 + 51 + ## [v0.1.0] - 2021-01-21 52 + 53 + This is Lumen's first tagged release. It contains a few fixes and dependency updates since the initial commit (2020-12-17). 54 + 55 + [Unreleased]: https://github.com/naim94a/lumen/compare/v0.4.0...HEAD 56 + [v0.4.0]: https://github.com/naim94a/lumen/compare/v0.3.0...v0.4.0 57 + [v0.3.0]: https://github.com/naim94a/lumen/compare/v0.2.0...v0.3.0 58 + [v0.2.0]: https://github.com/naim94a/lumen/compare/v0.1.0...v0.2.0 59 + [v0.1.0]: https://github.com/naim94a/lumen/releases/tag/v0.1.0
+908 -359
Cargo.lock
··· 3 version = 3 4 5 [[package]] 6 name = "aho-corasick" 7 - version = "0.7.19" 8 source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 10 dependencies = [ 11 "memchr", 12 ] 13 14 [[package]] 15 name = "async-trait" 16 - version = "0.1.57" 17 source = "registry+https://github.com/rust-lang/crates.io-index" 18 - checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" 19 dependencies = [ 20 "proc-macro2", 21 "quote", ··· 23 ] 24 25 [[package]] 26 - name = "atty" 27 - version = "0.2.14" 28 source = "registry+https://github.com/rust-lang/crates.io-index" 29 - checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 30 dependencies = [ 31 - "hermit-abi", 32 "libc", 33 - "winapi", 34 ] 35 36 [[package]] 37 - name = "autocfg" 38 - version = "1.1.0" 39 source = "registry+https://github.com/rust-lang/crates.io-index" 40 - checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 41 42 [[package]] 43 - name = "base64" 44 - version = "0.13.0" 45 source = "registry+https://github.com/rust-lang/crates.io-index" 46 - checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 47 48 [[package]] 49 name = "binascii" ··· 58 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 59 60 [[package]] 61 name = "block-buffer" 62 - version = "0.10.3" 63 source = "registry+https://github.com/rust-lang/crates.io-index" 64 - checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 65 dependencies = [ 66 "generic-array", 67 ] 68 69 [[package]] 70 - name = "buf_redux" 71 - version = "0.8.4" 72 source = "registry+https://github.com/rust-lang/crates.io-index" 73 - checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 74 - dependencies = [ 75 - "memchr", 76 - "safemem", 77 - ] 78 79 [[package]] 80 name = "byteorder" 81 - version = "1.4.3" 82 source = "registry+https://github.com/rust-lang/crates.io-index" 83 - checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 84 85 [[package]] 86 name = "bytes" 87 - version = "1.2.1" 88 source = "registry+https://github.com/rust-lang/crates.io-index" 89 - checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 90 91 [[package]] 92 name = "cc" 93 - version = "1.0.73" 94 source = "registry+https://github.com/rust-lang/crates.io-index" 95 - checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 96 97 [[package]] 98 name = "cfg-if" ··· 102 103 [[package]] 104 name = "clap" 105 - version = "4.0.13" 106 source = "registry+https://github.com/rust-lang/crates.io-index" 107 - checksum = "69d64e88428747154bd8bc378d178377ef4dace7a5735ca1f3855be72f2c2cb5" 108 dependencies = [ 109 - "atty", 110 - "bitflags", 111 "clap_lex", 112 "strsim", 113 - "termcolor", 114 ] 115 116 [[package]] 117 name = "clap_lex" 118 - version = "0.3.0" 119 source = "registry+https://github.com/rust-lang/crates.io-index" 120 - checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 121 - dependencies = [ 122 - "os_str_bytes", 123 - ] 124 125 [[package]] 126 name = "common" 127 version = "0.2.0" 128 dependencies = [ 129 "binascii", 130 "futures-util", 131 "log", 132 "native-tls", 133 "postgres-native-tls", 134 "serde", 135 "tokio", 136 "tokio-postgres", 137 "toml", ··· 140 141 [[package]] 142 name = "core-foundation" 143 - version = "0.9.3" 144 source = "registry+https://github.com/rust-lang/crates.io-index" 145 - checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 146 dependencies = [ 147 "core-foundation-sys", 148 "libc", ··· 150 151 [[package]] 152 name = "core-foundation-sys" 153 - version = "0.8.3" 154 source = "registry+https://github.com/rust-lang/crates.io-index" 155 - checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 156 157 [[package]] 158 name = "cpufeatures" 159 - version = "0.2.5" 160 source = "registry+https://github.com/rust-lang/crates.io-index" 161 - checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 162 dependencies = [ 163 "libc", 164 ] ··· 174 ] 175 176 [[package]] 177 name = "digest" 178 - version = "0.10.5" 179 source = "registry+https://github.com/rust-lang/crates.io-index" 180 - checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" 181 dependencies = [ 182 "block-buffer", 183 "crypto-common", ··· 185 ] 186 187 [[package]] 188 name = "env_logger" 189 - version = "0.7.1" 190 source = "registry+https://github.com/rust-lang/crates.io-index" 191 - checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 192 dependencies = [ 193 - "atty", 194 "humantime", 195 "log", 196 "regex", 197 "termcolor", 198 ] 199 200 [[package]] 201 name = "fallible-iterator" 202 version = "0.2.0" 203 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 205 206 [[package]] 207 name = "fastrand" 208 - version = "1.8.0" 209 source = "registry+https://github.com/rust-lang/crates.io-index" 210 - checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 211 - dependencies = [ 212 - "instant", 213 - ] 214 215 [[package]] 216 name = "fnv" ··· 235 236 [[package]] 237 name = "form_urlencoded" 238 - version = "1.1.0" 239 source = "registry+https://github.com/rust-lang/crates.io-index" 240 - checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 241 dependencies = [ 242 "percent-encoding", 243 ] 244 245 [[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", ··· 259 260 [[package]] 261 name = "futures-channel" 262 - version = "0.3.24" 263 source = "registry+https://github.com/rust-lang/crates.io-index" 264 - checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 265 dependencies = [ 266 "futures-core", 267 "futures-sink", ··· 269 270 [[package]] 271 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", ··· 286 287 [[package]] 288 name = "futures-io" 289 - version = "0.3.24" 290 source = "registry+https://github.com/rust-lang/crates.io-index" 291 - checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" 292 293 [[package]] 294 name = "futures-macro" 295 - version = "0.3.24" 296 source = "registry+https://github.com/rust-lang/crates.io-index" 297 - checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" 298 dependencies = [ 299 "proc-macro2", 300 "quote", ··· 303 304 [[package]] 305 name = "futures-sink" 306 - version = "0.3.24" 307 source = "registry+https://github.com/rust-lang/crates.io-index" 308 - checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 309 310 [[package]] 311 name = "futures-task" 312 - version = "0.3.24" 313 source = "registry+https://github.com/rust-lang/crates.io-index" 314 - checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 315 316 [[package]] 317 name = "futures-util" 318 - version = "0.3.24" 319 source = "registry+https://github.com/rust-lang/crates.io-index" 320 - checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 321 dependencies = [ 322 "futures-channel", 323 "futures-core", ··· 333 334 [[package]] 335 name = "generic-array" 336 - version = "0.14.6" 337 source = "registry+https://github.com/rust-lang/crates.io-index" 338 - checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 339 dependencies = [ 340 "typenum", 341 "version_check", ··· 343 344 [[package]] 345 name = "getrandom" 346 - version = "0.2.7" 347 source = "registry+https://github.com/rust-lang/crates.io-index" 348 - checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" 349 dependencies = [ 350 "cfg-if", 351 "libc", ··· 353 ] 354 355 [[package]] 356 name = "h2" 357 - version = "0.3.14" 358 source = "registry+https://github.com/rust-lang/crates.io-index" 359 - checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" 360 dependencies = [ 361 "bytes", 362 "fnv", ··· 373 374 [[package]] 375 name = "hashbrown" 376 - version = "0.12.3" 377 source = "registry+https://github.com/rust-lang/crates.io-index" 378 - checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 379 380 [[package]] 381 name = "headers" 382 - version = "0.3.8" 383 source = "registry+https://github.com/rust-lang/crates.io-index" 384 - checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" 385 dependencies = [ 386 "base64", 387 - "bitflags", 388 "bytes", 389 "headers-core", 390 "http", ··· 404 405 [[package]] 406 name = "hermit-abi" 407 - version = "0.1.19" 408 source = "registry+https://github.com/rust-lang/crates.io-index" 409 - checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 410 - dependencies = [ 411 - "libc", 412 - ] 413 414 [[package]] 415 name = "hmac" ··· 422 423 [[package]] 424 name = "http" 425 - version = "0.2.8" 426 source = "registry+https://github.com/rust-lang/crates.io-index" 427 - checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 428 dependencies = [ 429 "bytes", 430 "fnv", ··· 433 434 [[package]] 435 name = "http-body" 436 - version = "0.4.5" 437 source = "registry+https://github.com/rust-lang/crates.io-index" 438 - checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 439 dependencies = [ 440 "bytes", 441 "http", ··· 450 451 [[package]] 452 name = "httpdate" 453 - version = "1.0.2" 454 source = "registry+https://github.com/rust-lang/crates.io-index" 455 - checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 456 457 [[package]] 458 name = "humantime" 459 - version = "1.3.0" 460 source = "registry+https://github.com/rust-lang/crates.io-index" 461 - checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 462 - dependencies = [ 463 - "quick-error", 464 - ] 465 466 [[package]] 467 name = "hyper" 468 - version = "0.14.20" 469 source = "registry+https://github.com/rust-lang/crates.io-index" 470 - checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 471 dependencies = [ 472 "bytes", 473 "futures-channel", ··· 489 490 [[package]] 491 name = "idna" 492 - version = "0.3.0" 493 source = "registry+https://github.com/rust-lang/crates.io-index" 494 - checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 495 dependencies = [ 496 "unicode-bidi", 497 "unicode-normalization", ··· 499 500 [[package]] 501 name = "indexmap" 502 - version = "1.9.1" 503 source = "registry+https://github.com/rust-lang/crates.io-index" 504 - checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 505 dependencies = [ 506 - "autocfg", 507 "hashbrown", 508 ] 509 510 [[package]] 511 - name = "instant" 512 - version = "0.1.12" 513 source = "registry+https://github.com/rust-lang/crates.io-index" 514 - checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 515 dependencies = [ 516 - "cfg-if", 517 ] 518 519 [[package]] 520 name = "itoa" 521 - version = "1.0.4" 522 source = "registry+https://github.com/rust-lang/crates.io-index" 523 - checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 524 525 [[package]] 526 name = "lazy_static" ··· 530 531 [[package]] 532 name = "libc" 533 - version = "0.2.135" 534 source = "registry+https://github.com/rust-lang/crates.io-index" 535 - checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 536 537 [[package]] 538 name = "lock_api" 539 - version = "0.4.9" 540 source = "registry+https://github.com/rust-lang/crates.io-index" 541 - checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 542 dependencies = [ 543 "autocfg", 544 "scopeguard", ··· 546 547 [[package]] 548 name = "log" 549 - version = "0.4.17" 550 source = "registry+https://github.com/rust-lang/crates.io-index" 551 - checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 552 - dependencies = [ 553 - "cfg-if", 554 - ] 555 556 [[package]] 557 name = "lumen" ··· 562 "log", 563 "native-tls", 564 "pretty_env_logger", 565 "tokio", 566 "tokio-native-tls", 567 "warp", ··· 569 570 [[package]] 571 name = "md-5" 572 - version = "0.10.5" 573 source = "registry+https://github.com/rust-lang/crates.io-index" 574 - checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" 575 dependencies = [ 576 "digest", 577 ] 578 579 [[package]] 580 name = "memchr" 581 - version = "2.5.0" 582 source = "registry+https://github.com/rust-lang/crates.io-index" 583 - checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 584 585 [[package]] 586 name = "mime" 587 - version = "0.3.16" 588 source = "registry+https://github.com/rust-lang/crates.io-index" 589 - checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 590 591 [[package]] 592 name = "mime_guess" ··· 599 ] 600 601 [[package]] 602 name = "mio" 603 - version = "0.8.4" 604 source = "registry+https://github.com/rust-lang/crates.io-index" 605 - checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 606 dependencies = [ 607 "libc", 608 - "log", 609 "wasi", 610 - "windows-sys", 611 ] 612 613 [[package]] 614 - name = "multipart" 615 - version = "0.18.0" 616 source = "registry+https://github.com/rust-lang/crates.io-index" 617 - checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 618 dependencies = [ 619 - "buf_redux", 620 "httparse", 621 "log", 622 "mime", 623 - "mime_guess", 624 - "quick-error", 625 - "rand", 626 - "safemem", 627 - "tempfile", 628 - "twoway", 629 ] 630 631 [[package]] 632 name = "native-tls" 633 - version = "0.2.10" 634 source = "registry+https://github.com/rust-lang/crates.io-index" 635 - checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" 636 dependencies = [ 637 "lazy_static", 638 "libc", ··· 647 ] 648 649 [[package]] 650 name = "num_cpus" 651 - version = "1.13.1" 652 source = "registry+https://github.com/rust-lang/crates.io-index" 653 - checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 654 dependencies = [ 655 "hermit-abi", 656 "libc", 657 ] 658 659 [[package]] 660 name = "once_cell" 661 - version = "1.15.0" 662 source = "registry+https://github.com/rust-lang/crates.io-index" 663 - checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 664 665 [[package]] 666 name = "openssl" 667 - version = "0.10.42" 668 source = "registry+https://github.com/rust-lang/crates.io-index" 669 - checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" 670 dependencies = [ 671 - "bitflags", 672 "cfg-if", 673 "foreign-types", 674 "libc", ··· 679 680 [[package]] 681 name = "openssl-macros" 682 - version = "0.1.0" 683 source = "registry+https://github.com/rust-lang/crates.io-index" 684 - checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 685 dependencies = [ 686 "proc-macro2", 687 "quote", ··· 696 697 [[package]] 698 name = "openssl-sys" 699 - version = "0.9.76" 700 source = "registry+https://github.com/rust-lang/crates.io-index" 701 - checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" 702 dependencies = [ 703 - "autocfg", 704 "cc", 705 "libc", 706 "pkg-config", ··· 708 ] 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 - 716 - [[package]] 717 name = "parking_lot" 718 version = "0.12.1" 719 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 725 726 [[package]] 727 name = "parking_lot_core" 728 - version = "0.9.3" 729 source = "registry+https://github.com/rust-lang/crates.io-index" 730 - checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 731 dependencies = [ 732 "cfg-if", 733 "libc", 734 "redox_syscall", 735 "smallvec", 736 - "windows-sys", 737 ] 738 739 [[package]] 740 name = "percent-encoding" 741 - version = "2.2.0" 742 source = "registry+https://github.com/rust-lang/crates.io-index" 743 - checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 744 745 [[package]] 746 name = "phf" 747 - version = "0.11.1" 748 source = "registry+https://github.com/rust-lang/crates.io-index" 749 - checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" 750 dependencies = [ 751 "phf_shared", 752 ] 753 754 [[package]] 755 name = "phf_shared" 756 - version = "0.11.1" 757 source = "registry+https://github.com/rust-lang/crates.io-index" 758 - checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" 759 dependencies = [ 760 "siphasher", 761 ] 762 763 [[package]] 764 name = "pin-project" 765 - version = "1.0.12" 766 source = "registry+https://github.com/rust-lang/crates.io-index" 767 - checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 768 dependencies = [ 769 "pin-project-internal", 770 ] 771 772 [[package]] 773 name = "pin-project-internal" 774 - version = "1.0.12" 775 source = "registry+https://github.com/rust-lang/crates.io-index" 776 - checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 777 dependencies = [ 778 "proc-macro2", 779 "quote", ··· 782 783 [[package]] 784 name = "pin-project-lite" 785 - version = "0.2.9" 786 source = "registry+https://github.com/rust-lang/crates.io-index" 787 - checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 788 789 [[package]] 790 name = "pin-utils" ··· 794 795 [[package]] 796 name = "pkg-config" 797 - version = "0.3.25" 798 source = "registry+https://github.com/rust-lang/crates.io-index" 799 - checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 800 801 [[package]] 802 name = "postgres-native-tls" ··· 813 814 [[package]] 815 name = "postgres-protocol" 816 - version = "0.6.4" 817 source = "registry+https://github.com/rust-lang/crates.io-index" 818 - checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" 819 dependencies = [ 820 "base64", 821 "byteorder", ··· 831 832 [[package]] 833 name = "postgres-types" 834 - version = "0.2.4" 835 source = "registry+https://github.com/rust-lang/crates.io-index" 836 - checksum = "73d946ec7d256b04dfadc4e6a3292324e6f417124750fc5c0950f981b703a0f1" 837 dependencies = [ 838 "bytes", 839 "fallible-iterator", 840 "postgres-protocol", 841 ] 842 843 [[package]] 844 name = "ppv-lite86" 845 - version = "0.2.16" 846 source = "registry+https://github.com/rust-lang/crates.io-index" 847 - checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 848 849 [[package]] 850 name = "pretty_env_logger" 851 - version = "0.4.0" 852 source = "registry+https://github.com/rust-lang/crates.io-index" 853 - checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 854 dependencies = [ 855 "env_logger", 856 "log", ··· 858 859 [[package]] 860 name = "proc-macro2" 861 - version = "1.0.46" 862 source = "registry+https://github.com/rust-lang/crates.io-index" 863 - checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 864 dependencies = [ 865 "unicode-ident", 866 ] 867 868 [[package]] 869 - name = "quick-error" 870 - version = "1.2.3" 871 source = "registry+https://github.com/rust-lang/crates.io-index" 872 - checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 873 874 [[package]] 875 name = "quote" 876 - version = "1.0.21" 877 source = "registry+https://github.com/rust-lang/crates.io-index" 878 - checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 879 dependencies = [ 880 "proc-macro2", 881 ] ··· 912 913 [[package]] 914 name = "redox_syscall" 915 - version = "0.2.16" 916 source = "registry+https://github.com/rust-lang/crates.io-index" 917 - checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 918 dependencies = [ 919 - "bitflags", 920 ] 921 922 [[package]] 923 name = "regex" 924 - version = "1.6.0" 925 source = "registry+https://github.com/rust-lang/crates.io-index" 926 - checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 927 dependencies = [ 928 "aho-corasick", 929 "memchr", ··· 932 933 [[package]] 934 name = "regex-syntax" 935 - version = "0.6.27" 936 source = "registry+https://github.com/rust-lang/crates.io-index" 937 - checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 938 939 [[package]] 940 - name = "remove_dir_all" 941 - version = "0.5.3" 942 source = "registry+https://github.com/rust-lang/crates.io-index" 943 - checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 944 dependencies = [ 945 - "winapi", 946 ] 947 948 [[package]] 949 name = "rustls-pemfile" 950 - version = "0.2.1" 951 source = "registry+https://github.com/rust-lang/crates.io-index" 952 - checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 953 dependencies = [ 954 "base64", 955 ] 956 957 [[package]] 958 name = "ryu" 959 - version = "1.0.11" 960 source = "registry+https://github.com/rust-lang/crates.io-index" 961 - checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 962 963 [[package]] 964 - name = "safemem" 965 - version = "0.3.3" 966 source = "registry+https://github.com/rust-lang/crates.io-index" 967 - checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 968 969 [[package]] 970 - name = "schannel" 971 - version = "0.1.20" 972 source = "registry+https://github.com/rust-lang/crates.io-index" 973 - checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" 974 dependencies = [ 975 - "lazy_static", 976 - "windows-sys", 977 ] 978 979 [[package]] 980 name = "scoped-tls" 981 - version = "1.0.0" 982 source = "registry+https://github.com/rust-lang/crates.io-index" 983 - checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 984 985 [[package]] 986 name = "scopeguard" 987 - version = "1.1.0" 988 source = "registry+https://github.com/rust-lang/crates.io-index" 989 - checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 990 991 [[package]] 992 name = "security-framework" 993 - version = "2.7.0" 994 source = "registry+https://github.com/rust-lang/crates.io-index" 995 - checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" 996 dependencies = [ 997 - "bitflags", 998 "core-foundation", 999 "core-foundation-sys", 1000 "libc", ··· 1003 1004 [[package]] 1005 name = "security-framework-sys" 1006 - version = "2.6.1" 1007 source = "registry+https://github.com/rust-lang/crates.io-index" 1008 - checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 1009 dependencies = [ 1010 "core-foundation-sys", 1011 "libc", ··· 1013 1014 [[package]] 1015 name = "serde" 1016 - version = "1.0.145" 1017 source = "registry+https://github.com/rust-lang/crates.io-index" 1018 - checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 1019 dependencies = [ 1020 "serde_derive", 1021 ] 1022 1023 [[package]] 1024 name = "serde_derive" 1025 - version = "1.0.145" 1026 source = "registry+https://github.com/rust-lang/crates.io-index" 1027 - checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 1028 dependencies = [ 1029 "proc-macro2", 1030 "quote", ··· 1033 1034 [[package]] 1035 name = "serde_json" 1036 - version = "1.0.86" 1037 source = "registry+https://github.com/rust-lang/crates.io-index" 1038 - checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" 1039 dependencies = [ 1040 "itoa", 1041 "ryu", ··· 1043 ] 1044 1045 [[package]] 1046 name = "serde_urlencoded" 1047 version = "0.7.1" 1048 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1055 ] 1056 1057 [[package]] 1058 - name = "sha-1" 1059 - version = "0.10.0" 1060 - source = "registry+https://github.com/rust-lang/crates.io-index" 1061 - checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" 1062 - dependencies = [ 1063 - "cfg-if", 1064 - "cpufeatures", 1065 - "digest", 1066 - ] 1067 - 1068 - [[package]] 1069 name = "sha1" 1070 - version = "0.10.5" 1071 source = "registry+https://github.com/rust-lang/crates.io-index" 1072 - checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1073 dependencies = [ 1074 "cfg-if", 1075 "cpufeatures", ··· 1078 1079 [[package]] 1080 name = "sha2" 1081 - version = "0.10.6" 1082 source = "registry+https://github.com/rust-lang/crates.io-index" 1083 - checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" 1084 dependencies = [ 1085 "cfg-if", 1086 "cpufeatures", ··· 1089 1090 [[package]] 1091 name = "signal-hook-registry" 1092 - version = "1.4.0" 1093 source = "registry+https://github.com/rust-lang/crates.io-index" 1094 - checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1095 dependencies = [ 1096 "libc", 1097 ] 1098 1099 [[package]] 1100 name = "siphasher" 1101 - version = "0.3.10" 1102 source = "registry+https://github.com/rust-lang/crates.io-index" 1103 - checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" 1104 1105 [[package]] 1106 name = "slab" 1107 - version = "0.4.7" 1108 source = "registry+https://github.com/rust-lang/crates.io-index" 1109 - checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 1110 dependencies = [ 1111 "autocfg", 1112 ] 1113 1114 [[package]] 1115 name = "smallvec" 1116 - version = "1.10.0" 1117 source = "registry+https://github.com/rust-lang/crates.io-index" 1118 - checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1119 1120 [[package]] 1121 name = "socket2" 1122 - version = "0.4.7" 1123 source = "registry+https://github.com/rust-lang/crates.io-index" 1124 - checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1125 dependencies = [ 1126 "libc", 1127 - "winapi", 1128 ] 1129 1130 [[package]] 1131 name = "stringprep" 1132 - version = "0.1.2" 1133 source = "registry+https://github.com/rust-lang/crates.io-index" 1134 - checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" 1135 dependencies = [ 1136 "unicode-bidi", 1137 "unicode-normalization", 1138 ] 1139 1140 [[package]] 1141 name = "strsim" 1142 - version = "0.10.0" 1143 source = "registry+https://github.com/rust-lang/crates.io-index" 1144 - checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1145 1146 [[package]] 1147 name = "subtle" 1148 - version = "2.4.1" 1149 source = "registry+https://github.com/rust-lang/crates.io-index" 1150 - checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1151 1152 [[package]] 1153 name = "syn" 1154 - version = "1.0.102" 1155 source = "registry+https://github.com/rust-lang/crates.io-index" 1156 - checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" 1157 dependencies = [ 1158 "proc-macro2", 1159 "quote", ··· 1162 1163 [[package]] 1164 name = "tempfile" 1165 - version = "3.3.0" 1166 source = "registry+https://github.com/rust-lang/crates.io-index" 1167 - checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1168 dependencies = [ 1169 "cfg-if", 1170 "fastrand", 1171 - "libc", 1172 - "redox_syscall", 1173 - "remove_dir_all", 1174 - "winapi", 1175 ] 1176 1177 [[package]] 1178 name = "termcolor" 1179 - version = "1.1.3" 1180 source = "registry+https://github.com/rust-lang/crates.io-index" 1181 - checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1182 dependencies = [ 1183 "winapi-util", 1184 ] 1185 1186 [[package]] 1187 name = "thiserror" 1188 - version = "1.0.37" 1189 source = "registry+https://github.com/rust-lang/crates.io-index" 1190 - checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 1191 dependencies = [ 1192 "thiserror-impl", 1193 ] 1194 1195 [[package]] 1196 name = "thiserror-impl" 1197 - version = "1.0.37" 1198 source = "registry+https://github.com/rust-lang/crates.io-index" 1199 - checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 1200 dependencies = [ 1201 "proc-macro2", 1202 "quote", ··· 1204 ] 1205 1206 [[package]] 1207 name = "tinyvec" 1208 version = "1.6.0" 1209 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1214 1215 [[package]] 1216 name = "tinyvec_macros" 1217 - version = "0.1.0" 1218 source = "registry+https://github.com/rust-lang/crates.io-index" 1219 - checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1220 1221 [[package]] 1222 name = "tokio" 1223 - version = "1.21.2" 1224 source = "registry+https://github.com/rust-lang/crates.io-index" 1225 - checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 1226 dependencies = [ 1227 - "autocfg", 1228 "bytes", 1229 "libc", 1230 - "memchr", 1231 "mio", 1232 "num_cpus", 1233 "parking_lot", ··· 1235 "signal-hook-registry", 1236 "socket2", 1237 "tokio-macros", 1238 - "winapi", 1239 ] 1240 1241 [[package]] 1242 name = "tokio-macros" 1243 - version = "1.8.0" 1244 source = "registry+https://github.com/rust-lang/crates.io-index" 1245 - checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 1246 dependencies = [ 1247 "proc-macro2", 1248 "quote", ··· 1251 1252 [[package]] 1253 name = "tokio-native-tls" 1254 - version = "0.3.0" 1255 source = "registry+https://github.com/rust-lang/crates.io-index" 1256 - checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1257 dependencies = [ 1258 "native-tls", 1259 "tokio", ··· 1261 1262 [[package]] 1263 name = "tokio-postgres" 1264 - version = "0.7.7" 1265 source = "registry+https://github.com/rust-lang/crates.io-index" 1266 - checksum = "29a12c1b3e0704ae7dfc25562629798b29c72e6b1d0a681b6f29ab4ae5e7f7bf" 1267 dependencies = [ 1268 "async-trait", 1269 "byteorder", ··· 1278 "pin-project-lite", 1279 "postgres-protocol", 1280 "postgres-types", 1281 "socket2", 1282 "tokio", 1283 "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", ··· 1296 1297 [[package]] 1298 name = "tokio-tungstenite" 1299 - version = "0.17.2" 1300 source = "registry+https://github.com/rust-lang/crates.io-index" 1301 - checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" 1302 dependencies = [ 1303 "futures-util", 1304 "log", ··· 1308 1309 [[package]] 1310 name = "tokio-util" 1311 - version = "0.7.4" 1312 source = "registry+https://github.com/rust-lang/crates.io-index" 1313 - checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 1314 dependencies = [ 1315 "bytes", 1316 "futures-core", ··· 1322 1323 [[package]] 1324 name = "toml" 1325 - version = "0.5.9" 1326 source = "registry+https://github.com/rust-lang/crates.io-index" 1327 - checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1328 dependencies = [ 1329 "serde", 1330 ] 1331 1332 [[package]] ··· 1337 1338 [[package]] 1339 name = "tracing" 1340 - version = "0.1.37" 1341 source = "registry+https://github.com/rust-lang/crates.io-index" 1342 - checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1343 dependencies = [ 1344 - "cfg-if", 1345 "log", 1346 "pin-project-lite", 1347 "tracing-core", ··· 1349 1350 [[package]] 1351 name = "tracing-core" 1352 - version = "0.1.30" 1353 source = "registry+https://github.com/rust-lang/crates.io-index" 1354 - checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1355 dependencies = [ 1356 "once_cell", 1357 ] 1358 1359 [[package]] 1360 name = "try-lock" 1361 - version = "0.2.3" 1362 source = "registry+https://github.com/rust-lang/crates.io-index" 1363 - checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1364 1365 [[package]] 1366 name = "tungstenite" 1367 - version = "0.17.3" 1368 source = "registry+https://github.com/rust-lang/crates.io-index" 1369 - checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" 1370 dependencies = [ 1371 - "base64", 1372 "byteorder", 1373 "bytes", 1374 "http", 1375 "httparse", 1376 "log", 1377 "rand", 1378 - "sha-1", 1379 "thiserror", 1380 "url", 1381 "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 - ] 1392 - 1393 - [[package]] 1394 name = "typenum" 1395 - version = "1.15.0" 1396 source = "registry+https://github.com/rust-lang/crates.io-index" 1397 - checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1398 1399 [[package]] 1400 name = "unicase" 1401 - version = "2.6.0" 1402 source = "registry+https://github.com/rust-lang/crates.io-index" 1403 - checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1404 dependencies = [ 1405 "version_check", 1406 ] 1407 1408 [[package]] 1409 name = "unicode-bidi" 1410 - version = "0.3.8" 1411 source = "registry+https://github.com/rust-lang/crates.io-index" 1412 - checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1413 1414 [[package]] 1415 name = "unicode-ident" 1416 - version = "1.0.5" 1417 source = "registry+https://github.com/rust-lang/crates.io-index" 1418 - checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1419 1420 [[package]] 1421 name = "unicode-normalization" 1422 - version = "0.1.22" 1423 source = "registry+https://github.com/rust-lang/crates.io-index" 1424 - checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1425 dependencies = [ 1426 "tinyvec", 1427 ] 1428 1429 [[package]] 1430 name = "url" 1431 - version = "2.3.1" 1432 source = "registry+https://github.com/rust-lang/crates.io-index" 1433 - checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1434 dependencies = [ 1435 "form_urlencoded", 1436 "idna", ··· 1444 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1445 1446 [[package]] 1447 name = "vcpkg" 1448 version = "0.2.15" 1449 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1457 1458 [[package]] 1459 name = "want" 1460 - version = "0.3.0" 1461 source = "registry+https://github.com/rust-lang/crates.io-index" 1462 - checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1463 dependencies = [ 1464 - "log", 1465 "try-lock", 1466 ] 1467 1468 [[package]] 1469 name = "warp" 1470 - version = "0.3.3" 1471 source = "registry+https://github.com/rust-lang/crates.io-index" 1472 - checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" 1473 dependencies = [ 1474 "bytes", 1475 "futures-channel", ··· 1480 "log", 1481 "mime", 1482 "mime_guess", 1483 - "multipart", 1484 "percent-encoding", 1485 "pin-project", 1486 "rustls-pemfile", ··· 1503 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1504 1505 [[package]] 1506 name = "winapi" 1507 version = "0.3.9" 1508 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1520 1521 [[package]] 1522 name = "winapi-util" 1523 - version = "0.1.5" 1524 source = "registry+https://github.com/rust-lang/crates.io-index" 1525 - checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1526 dependencies = [ 1527 "winapi", 1528 ] ··· 1535 1536 [[package]] 1537 name = "windows-sys" 1538 - version = "0.36.1" 1539 source = "registry+https://github.com/rust-lang/crates.io-index" 1540 - checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1541 dependencies = [ 1542 - "windows_aarch64_msvc", 1543 - "windows_i686_gnu", 1544 - "windows_i686_msvc", 1545 - "windows_x86_64_gnu", 1546 - "windows_x86_64_msvc", 1547 ] 1548 1549 [[package]] 1550 name = "windows_aarch64_msvc" 1551 - version = "0.36.1" 1552 source = "registry+https://github.com/rust-lang/crates.io-index" 1553 - checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1554 1555 [[package]] 1556 name = "windows_i686_gnu" 1557 - version = "0.36.1" 1558 source = "registry+https://github.com/rust-lang/crates.io-index" 1559 - checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1560 1561 [[package]] 1562 name = "windows_i686_msvc" 1563 - version = "0.36.1" 1564 source = "registry+https://github.com/rust-lang/crates.io-index" 1565 - checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1566 1567 [[package]] 1568 name = "windows_x86_64_gnu" 1569 - version = "0.36.1" 1570 source = "registry+https://github.com/rust-lang/crates.io-index" 1571 - checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1572 1573 [[package]] 1574 name = "windows_x86_64_msvc" 1575 - version = "0.36.1" 1576 source = "registry+https://github.com/rust-lang/crates.io-index" 1577 - checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
··· 3 version = 3 4 5 [[package]] 6 + name = "addr2line" 7 + version = "0.21.0" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 + dependencies = [ 11 + "gimli", 12 + ] 13 + 14 + [[package]] 15 + name = "adler" 16 + version = "1.0.2" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 + 20 + [[package]] 21 name = "aho-corasick" 22 + version = "1.1.3" 23 source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 dependencies = [ 26 "memchr", 27 ] 28 29 [[package]] 30 + name = "anstream" 31 + version = "0.6.13" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 34 + dependencies = [ 35 + "anstyle", 36 + "anstyle-parse", 37 + "anstyle-query", 38 + "anstyle-wincon", 39 + "colorchoice", 40 + "utf8parse", 41 + ] 42 + 43 + [[package]] 44 + name = "anstyle" 45 + version = "1.0.6" 46 + source = "registry+https://github.com/rust-lang/crates.io-index" 47 + checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 48 + 49 + [[package]] 50 + name = "anstyle-parse" 51 + version = "0.2.3" 52 + source = "registry+https://github.com/rust-lang/crates.io-index" 53 + checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 54 + dependencies = [ 55 + "utf8parse", 56 + ] 57 + 58 + [[package]] 59 + name = "anstyle-query" 60 + version = "1.0.2" 61 + source = "registry+https://github.com/rust-lang/crates.io-index" 62 + checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 63 + dependencies = [ 64 + "windows-sys 0.52.0", 65 + ] 66 + 67 + [[package]] 68 + name = "anstyle-wincon" 69 + version = "3.0.2" 70 + source = "registry+https://github.com/rust-lang/crates.io-index" 71 + checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 72 + dependencies = [ 73 + "anstyle", 74 + "windows-sys 0.52.0", 75 + ] 76 + 77 + [[package]] 78 + name = "anyhow" 79 + version = "1.0.81" 80 + source = "registry+https://github.com/rust-lang/crates.io-index" 81 + checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" 82 + 83 + [[package]] 84 name = "async-trait" 85 + version = "0.1.78" 86 source = "registry+https://github.com/rust-lang/crates.io-index" 87 + checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" 88 dependencies = [ 89 "proc-macro2", 90 "quote", ··· 92 ] 93 94 [[package]] 95 + name = "autocfg" 96 + version = "1.1.0" 97 + source = "registry+https://github.com/rust-lang/crates.io-index" 98 + checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 99 + 100 + [[package]] 101 + name = "backtrace" 102 + version = "0.3.70" 103 source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "95d8e92cac0961e91dbd517496b00f7e9b92363dbe6d42c3198268323798860c" 105 dependencies = [ 106 + "addr2line", 107 + "cc", 108 + "cfg-if", 109 "libc", 110 + "miniz_oxide", 111 + "object", 112 + "rustc-demangle", 113 ] 114 115 [[package]] 116 + name = "base64" 117 + version = "0.21.7" 118 source = "registry+https://github.com/rust-lang/crates.io-index" 119 + checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 120 121 [[package]] 122 + name = "bb8" 123 + version = "0.8.3" 124 source = "registry+https://github.com/rust-lang/crates.io-index" 125 + checksum = "df7c2093d15d6a1d33b1f972e1c5ea3177748742b97a5f392aa83a65262c6780" 126 + dependencies = [ 127 + "async-trait", 128 + "futures-channel", 129 + "futures-util", 130 + "parking_lot", 131 + "tokio", 132 + ] 133 134 [[package]] 135 name = "binascii" ··· 144 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 145 146 [[package]] 147 + name = "bitflags" 148 + version = "2.5.0" 149 + source = "registry+https://github.com/rust-lang/crates.io-index" 150 + checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 151 + 152 + [[package]] 153 name = "block-buffer" 154 + version = "0.10.4" 155 source = "registry+https://github.com/rust-lang/crates.io-index" 156 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 157 dependencies = [ 158 "generic-array", 159 ] 160 161 [[package]] 162 + name = "bumpalo" 163 + version = "3.15.4" 164 source = "registry+https://github.com/rust-lang/crates.io-index" 165 + checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" 166 167 [[package]] 168 name = "byteorder" 169 + version = "1.5.0" 170 source = "registry+https://github.com/rust-lang/crates.io-index" 171 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 172 173 [[package]] 174 name = "bytes" 175 + version = "1.5.0" 176 source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 178 179 [[package]] 180 name = "cc" 181 + version = "1.0.90" 182 source = "registry+https://github.com/rust-lang/crates.io-index" 183 + checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 184 185 [[package]] 186 name = "cfg-if" ··· 190 191 [[package]] 192 name = "clap" 193 + version = "4.5.3" 194 + source = "registry+https://github.com/rust-lang/crates.io-index" 195 + checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" 196 + dependencies = [ 197 + "clap_builder", 198 + ] 199 + 200 + [[package]] 201 + name = "clap_builder" 202 + version = "4.5.2" 203 source = "registry+https://github.com/rust-lang/crates.io-index" 204 + checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 205 dependencies = [ 206 + "anstream", 207 + "anstyle", 208 "clap_lex", 209 "strsim", 210 ] 211 212 [[package]] 213 name = "clap_lex" 214 + version = "0.7.0" 215 source = "registry+https://github.com/rust-lang/crates.io-index" 216 + checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 217 + 218 + [[package]] 219 + name = "colorchoice" 220 + version = "1.0.0" 221 + source = "registry+https://github.com/rust-lang/crates.io-index" 222 + checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 223 224 [[package]] 225 name = "common" 226 version = "0.2.0" 227 dependencies = [ 228 + "anyhow", 229 "binascii", 230 + "diesel", 231 + "diesel-async", 232 "futures-util", 233 "log", 234 "native-tls", 235 + "pbkdf2", 236 "postgres-native-tls", 237 + "prometheus-client", 238 + "rand", 239 "serde", 240 + "sha2", 241 + "time", 242 "tokio", 243 "tokio-postgres", 244 "toml", ··· 247 248 [[package]] 249 name = "core-foundation" 250 + version = "0.9.4" 251 source = "registry+https://github.com/rust-lang/crates.io-index" 252 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 253 dependencies = [ 254 "core-foundation-sys", 255 "libc", ··· 257 258 [[package]] 259 name = "core-foundation-sys" 260 + version = "0.8.6" 261 source = "registry+https://github.com/rust-lang/crates.io-index" 262 + checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 263 264 [[package]] 265 name = "cpufeatures" 266 + version = "0.2.12" 267 source = "registry+https://github.com/rust-lang/crates.io-index" 268 + checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 269 dependencies = [ 270 "libc", 271 ] ··· 281 ] 282 283 [[package]] 284 + name = "data-encoding" 285 + version = "2.5.0" 286 + source = "registry+https://github.com/rust-lang/crates.io-index" 287 + checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" 288 + 289 + [[package]] 290 + name = "deranged" 291 + version = "0.3.11" 292 + source = "registry+https://github.com/rust-lang/crates.io-index" 293 + checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 294 + dependencies = [ 295 + "powerfmt", 296 + ] 297 + 298 + [[package]] 299 + name = "diesel" 300 + version = "2.1.5" 301 + source = "registry+https://github.com/rust-lang/crates.io-index" 302 + checksum = "03fc05c17098f21b89bc7d98fe1dd3cce2c11c2ad8e145f2a44fe08ed28eb559" 303 + dependencies = [ 304 + "bitflags 2.5.0", 305 + "byteorder", 306 + "diesel_derives", 307 + "itoa", 308 + "time", 309 + ] 310 + 311 + [[package]] 312 + name = "diesel-async" 313 + version = "0.4.1" 314 + source = "registry+https://github.com/rust-lang/crates.io-index" 315 + checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" 316 + dependencies = [ 317 + "async-trait", 318 + "bb8", 319 + "diesel", 320 + "futures-util", 321 + "scoped-futures", 322 + "tokio", 323 + "tokio-postgres", 324 + ] 325 + 326 + [[package]] 327 + name = "diesel_derives" 328 + version = "2.1.3" 329 + source = "registry+https://github.com/rust-lang/crates.io-index" 330 + checksum = "5d02eecb814ae714ffe61ddc2db2dd03e6c49a42e269b5001355500d431cce0c" 331 + dependencies = [ 332 + "diesel_table_macro_syntax", 333 + "proc-macro2", 334 + "quote", 335 + "syn", 336 + ] 337 + 338 + [[package]] 339 + name = "diesel_table_macro_syntax" 340 + version = "0.1.0" 341 + source = "registry+https://github.com/rust-lang/crates.io-index" 342 + checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" 343 + dependencies = [ 344 + "syn", 345 + ] 346 + 347 + [[package]] 348 name = "digest" 349 + version = "0.10.7" 350 source = "registry+https://github.com/rust-lang/crates.io-index" 351 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 352 dependencies = [ 353 "block-buffer", 354 "crypto-common", ··· 356 ] 357 358 [[package]] 359 + name = "dtoa" 360 + version = "1.0.9" 361 + source = "registry+https://github.com/rust-lang/crates.io-index" 362 + checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" 363 + 364 + [[package]] 365 + name = "encoding_rs" 366 + version = "0.8.33" 367 + source = "registry+https://github.com/rust-lang/crates.io-index" 368 + checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" 369 + dependencies = [ 370 + "cfg-if", 371 + ] 372 + 373 + [[package]] 374 name = "env_logger" 375 + version = "0.10.2" 376 source = "registry+https://github.com/rust-lang/crates.io-index" 377 + checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" 378 dependencies = [ 379 "humantime", 380 + "is-terminal", 381 "log", 382 "regex", 383 "termcolor", 384 ] 385 386 [[package]] 387 + name = "equivalent" 388 + version = "1.0.1" 389 + source = "registry+https://github.com/rust-lang/crates.io-index" 390 + checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 391 + 392 + [[package]] 393 + name = "errno" 394 + version = "0.3.8" 395 + source = "registry+https://github.com/rust-lang/crates.io-index" 396 + checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 397 + dependencies = [ 398 + "libc", 399 + "windows-sys 0.52.0", 400 + ] 401 + 402 + [[package]] 403 name = "fallible-iterator" 404 version = "0.2.0" 405 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 407 408 [[package]] 409 name = "fastrand" 410 + version = "2.0.1" 411 + source = "registry+https://github.com/rust-lang/crates.io-index" 412 + checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 413 + 414 + [[package]] 415 + name = "finl_unicode" 416 + version = "1.2.0" 417 source = "registry+https://github.com/rust-lang/crates.io-index" 418 + checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" 419 420 [[package]] 421 name = "fnv" ··· 440 441 [[package]] 442 name = "form_urlencoded" 443 + version = "1.2.1" 444 source = "registry+https://github.com/rust-lang/crates.io-index" 445 + checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 446 dependencies = [ 447 "percent-encoding", 448 ] 449 450 [[package]] 451 name = "futures" 452 + version = "0.3.30" 453 source = "registry+https://github.com/rust-lang/crates.io-index" 454 + checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 455 dependencies = [ 456 "futures-channel", 457 "futures-core", ··· 464 465 [[package]] 466 name = "futures-channel" 467 + version = "0.3.30" 468 source = "registry+https://github.com/rust-lang/crates.io-index" 469 + checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 470 dependencies = [ 471 "futures-core", 472 "futures-sink", ··· 474 475 [[package]] 476 name = "futures-core" 477 + version = "0.3.30" 478 source = "registry+https://github.com/rust-lang/crates.io-index" 479 + checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 480 481 [[package]] 482 name = "futures-executor" 483 + version = "0.3.30" 484 source = "registry+https://github.com/rust-lang/crates.io-index" 485 + checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 486 dependencies = [ 487 "futures-core", 488 "futures-task", ··· 491 492 [[package]] 493 name = "futures-io" 494 + version = "0.3.30" 495 source = "registry+https://github.com/rust-lang/crates.io-index" 496 + checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 497 498 [[package]] 499 name = "futures-macro" 500 + version = "0.3.30" 501 source = "registry+https://github.com/rust-lang/crates.io-index" 502 + checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 503 dependencies = [ 504 "proc-macro2", 505 "quote", ··· 508 509 [[package]] 510 name = "futures-sink" 511 + version = "0.3.30" 512 source = "registry+https://github.com/rust-lang/crates.io-index" 513 + checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 514 515 [[package]] 516 name = "futures-task" 517 + version = "0.3.30" 518 source = "registry+https://github.com/rust-lang/crates.io-index" 519 + checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 520 521 [[package]] 522 name = "futures-util" 523 + version = "0.3.30" 524 source = "registry+https://github.com/rust-lang/crates.io-index" 525 + checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 526 dependencies = [ 527 "futures-channel", 528 "futures-core", ··· 538 539 [[package]] 540 name = "generic-array" 541 + version = "0.14.7" 542 source = "registry+https://github.com/rust-lang/crates.io-index" 543 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 544 dependencies = [ 545 "typenum", 546 "version_check", ··· 548 549 [[package]] 550 name = "getrandom" 551 + version = "0.2.12" 552 source = "registry+https://github.com/rust-lang/crates.io-index" 553 + checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 554 dependencies = [ 555 "cfg-if", 556 "libc", ··· 558 ] 559 560 [[package]] 561 + name = "gimli" 562 + version = "0.28.1" 563 + source = "registry+https://github.com/rust-lang/crates.io-index" 564 + checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 565 + 566 + [[package]] 567 name = "h2" 568 + version = "0.3.25" 569 source = "registry+https://github.com/rust-lang/crates.io-index" 570 + checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" 571 dependencies = [ 572 "bytes", 573 "fnv", ··· 584 585 [[package]] 586 name = "hashbrown" 587 + version = "0.14.3" 588 source = "registry+https://github.com/rust-lang/crates.io-index" 589 + checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 590 591 [[package]] 592 name = "headers" 593 + version = "0.3.9" 594 source = "registry+https://github.com/rust-lang/crates.io-index" 595 + checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" 596 dependencies = [ 597 "base64", 598 "bytes", 599 "headers-core", 600 "http", ··· 614 615 [[package]] 616 name = "hermit-abi" 617 + version = "0.3.9" 618 source = "registry+https://github.com/rust-lang/crates.io-index" 619 + checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 620 621 [[package]] 622 name = "hmac" ··· 629 630 [[package]] 631 name = "http" 632 + version = "0.2.12" 633 source = "registry+https://github.com/rust-lang/crates.io-index" 634 + checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 635 dependencies = [ 636 "bytes", 637 "fnv", ··· 640 641 [[package]] 642 name = "http-body" 643 + version = "0.4.6" 644 source = "registry+https://github.com/rust-lang/crates.io-index" 645 + checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 646 dependencies = [ 647 "bytes", 648 "http", ··· 657 658 [[package]] 659 name = "httpdate" 660 + version = "1.0.3" 661 source = "registry+https://github.com/rust-lang/crates.io-index" 662 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 663 664 [[package]] 665 name = "humantime" 666 + version = "2.1.0" 667 source = "registry+https://github.com/rust-lang/crates.io-index" 668 + checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 669 670 [[package]] 671 name = "hyper" 672 + version = "0.14.28" 673 source = "registry+https://github.com/rust-lang/crates.io-index" 674 + checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" 675 dependencies = [ 676 "bytes", 677 "futures-channel", ··· 693 694 [[package]] 695 name = "idna" 696 + version = "0.5.0" 697 source = "registry+https://github.com/rust-lang/crates.io-index" 698 + checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 699 dependencies = [ 700 "unicode-bidi", 701 "unicode-normalization", ··· 703 704 [[package]] 705 name = "indexmap" 706 + version = "2.2.5" 707 source = "registry+https://github.com/rust-lang/crates.io-index" 708 + checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" 709 dependencies = [ 710 + "equivalent", 711 "hashbrown", 712 ] 713 714 [[package]] 715 + name = "is-terminal" 716 + version = "0.4.12" 717 source = "registry+https://github.com/rust-lang/crates.io-index" 718 + checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" 719 dependencies = [ 720 + "hermit-abi", 721 + "libc", 722 + "windows-sys 0.52.0", 723 ] 724 725 [[package]] 726 name = "itoa" 727 + version = "1.0.10" 728 + source = "registry+https://github.com/rust-lang/crates.io-index" 729 + checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 730 + 731 + [[package]] 732 + name = "js-sys" 733 + version = "0.3.69" 734 source = "registry+https://github.com/rust-lang/crates.io-index" 735 + checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 736 + dependencies = [ 737 + "wasm-bindgen", 738 + ] 739 740 [[package]] 741 name = "lazy_static" ··· 745 746 [[package]] 747 name = "libc" 748 + version = "0.2.153" 749 source = "registry+https://github.com/rust-lang/crates.io-index" 750 + checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 751 + 752 + [[package]] 753 + name = "linux-raw-sys" 754 + version = "0.4.13" 755 + source = "registry+https://github.com/rust-lang/crates.io-index" 756 + checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 757 758 [[package]] 759 name = "lock_api" 760 + version = "0.4.11" 761 source = "registry+https://github.com/rust-lang/crates.io-index" 762 + checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 763 dependencies = [ 764 "autocfg", 765 "scopeguard", ··· 767 768 [[package]] 769 name = "log" 770 + version = "0.4.21" 771 source = "registry+https://github.com/rust-lang/crates.io-index" 772 + checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 773 774 [[package]] 775 name = "lumen" ··· 780 "log", 781 "native-tls", 782 "pretty_env_logger", 783 + "prometheus-client", 784 + "rpassword", 785 "tokio", 786 "tokio-native-tls", 787 "warp", ··· 789 790 [[package]] 791 name = "md-5" 792 + version = "0.10.6" 793 source = "registry+https://github.com/rust-lang/crates.io-index" 794 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 795 dependencies = [ 796 + "cfg-if", 797 "digest", 798 ] 799 800 [[package]] 801 name = "memchr" 802 + version = "2.7.1" 803 source = "registry+https://github.com/rust-lang/crates.io-index" 804 + checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 805 806 [[package]] 807 name = "mime" 808 + version = "0.3.17" 809 source = "registry+https://github.com/rust-lang/crates.io-index" 810 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 811 812 [[package]] 813 name = "mime_guess" ··· 820 ] 821 822 [[package]] 823 + name = "miniz_oxide" 824 + version = "0.7.2" 825 + source = "registry+https://github.com/rust-lang/crates.io-index" 826 + checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 827 + dependencies = [ 828 + "adler", 829 + ] 830 + 831 + [[package]] 832 name = "mio" 833 + version = "0.8.11" 834 source = "registry+https://github.com/rust-lang/crates.io-index" 835 + checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 836 dependencies = [ 837 "libc", 838 "wasi", 839 + "windows-sys 0.48.0", 840 ] 841 842 [[package]] 843 + name = "multer" 844 + version = "2.1.0" 845 source = "registry+https://github.com/rust-lang/crates.io-index" 846 + checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" 847 dependencies = [ 848 + "bytes", 849 + "encoding_rs", 850 + "futures-util", 851 + "http", 852 "httparse", 853 "log", 854 + "memchr", 855 "mime", 856 + "spin", 857 + "version_check", 858 ] 859 860 [[package]] 861 name = "native-tls" 862 + version = "0.2.11" 863 source = "registry+https://github.com/rust-lang/crates.io-index" 864 + checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 865 dependencies = [ 866 "lazy_static", 867 "libc", ··· 876 ] 877 878 [[package]] 879 + name = "num-conv" 880 + version = "0.1.0" 881 + source = "registry+https://github.com/rust-lang/crates.io-index" 882 + checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 883 + 884 + [[package]] 885 name = "num_cpus" 886 + version = "1.16.0" 887 source = "registry+https://github.com/rust-lang/crates.io-index" 888 + checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 889 dependencies = [ 890 "hermit-abi", 891 "libc", 892 ] 893 894 [[package]] 895 + name = "object" 896 + version = "0.32.2" 897 + source = "registry+https://github.com/rust-lang/crates.io-index" 898 + checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 899 + dependencies = [ 900 + "memchr", 901 + ] 902 + 903 + [[package]] 904 name = "once_cell" 905 + version = "1.19.0" 906 source = "registry+https://github.com/rust-lang/crates.io-index" 907 + checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 908 909 [[package]] 910 name = "openssl" 911 + version = "0.10.64" 912 source = "registry+https://github.com/rust-lang/crates.io-index" 913 + checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" 914 dependencies = [ 915 + "bitflags 2.5.0", 916 "cfg-if", 917 "foreign-types", 918 "libc", ··· 923 924 [[package]] 925 name = "openssl-macros" 926 + version = "0.1.1" 927 source = "registry+https://github.com/rust-lang/crates.io-index" 928 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 929 dependencies = [ 930 "proc-macro2", 931 "quote", ··· 940 941 [[package]] 942 name = "openssl-sys" 943 + version = "0.9.101" 944 source = "registry+https://github.com/rust-lang/crates.io-index" 945 + checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" 946 dependencies = [ 947 "cc", 948 "libc", 949 "pkg-config", ··· 951 ] 952 953 [[package]] 954 name = "parking_lot" 955 version = "0.12.1" 956 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 962 963 [[package]] 964 name = "parking_lot_core" 965 + version = "0.9.9" 966 source = "registry+https://github.com/rust-lang/crates.io-index" 967 + checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 968 dependencies = [ 969 "cfg-if", 970 "libc", 971 "redox_syscall", 972 "smallvec", 973 + "windows-targets 0.48.5", 974 + ] 975 + 976 + [[package]] 977 + name = "pbkdf2" 978 + version = "0.12.2" 979 + source = "registry+https://github.com/rust-lang/crates.io-index" 980 + checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 981 + dependencies = [ 982 + "digest", 983 + "hmac", 984 ] 985 986 [[package]] 987 name = "percent-encoding" 988 + version = "2.3.1" 989 source = "registry+https://github.com/rust-lang/crates.io-index" 990 + checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 991 992 [[package]] 993 name = "phf" 994 + version = "0.11.2" 995 source = "registry+https://github.com/rust-lang/crates.io-index" 996 + checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" 997 dependencies = [ 998 "phf_shared", 999 ] 1000 1001 [[package]] 1002 name = "phf_shared" 1003 + version = "0.11.2" 1004 source = "registry+https://github.com/rust-lang/crates.io-index" 1005 + checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" 1006 dependencies = [ 1007 "siphasher", 1008 ] 1009 1010 [[package]] 1011 name = "pin-project" 1012 + version = "1.1.5" 1013 source = "registry+https://github.com/rust-lang/crates.io-index" 1014 + checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1015 dependencies = [ 1016 "pin-project-internal", 1017 ] 1018 1019 [[package]] 1020 name = "pin-project-internal" 1021 + version = "1.1.5" 1022 source = "registry+https://github.com/rust-lang/crates.io-index" 1023 + checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1024 dependencies = [ 1025 "proc-macro2", 1026 "quote", ··· 1029 1030 [[package]] 1031 name = "pin-project-lite" 1032 + version = "0.2.13" 1033 source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 1035 1036 [[package]] 1037 name = "pin-utils" ··· 1041 1042 [[package]] 1043 name = "pkg-config" 1044 + version = "0.3.30" 1045 source = "registry+https://github.com/rust-lang/crates.io-index" 1046 + checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1047 1048 [[package]] 1049 name = "postgres-native-tls" ··· 1060 1061 [[package]] 1062 name = "postgres-protocol" 1063 + version = "0.6.6" 1064 source = "registry+https://github.com/rust-lang/crates.io-index" 1065 + checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" 1066 dependencies = [ 1067 "base64", 1068 "byteorder", ··· 1078 1079 [[package]] 1080 name = "postgres-types" 1081 + version = "0.2.6" 1082 source = "registry+https://github.com/rust-lang/crates.io-index" 1083 + checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" 1084 dependencies = [ 1085 "bytes", 1086 "fallible-iterator", 1087 "postgres-protocol", 1088 ] 1089 + 1090 + [[package]] 1091 + name = "powerfmt" 1092 + version = "0.2.0" 1093 + source = "registry+https://github.com/rust-lang/crates.io-index" 1094 + checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1095 1096 [[package]] 1097 name = "ppv-lite86" 1098 + version = "0.2.17" 1099 source = "registry+https://github.com/rust-lang/crates.io-index" 1100 + checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1101 1102 [[package]] 1103 name = "pretty_env_logger" 1104 + version = "0.5.0" 1105 source = "registry+https://github.com/rust-lang/crates.io-index" 1106 + checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" 1107 dependencies = [ 1108 "env_logger", 1109 "log", ··· 1111 1112 [[package]] 1113 name = "proc-macro2" 1114 + version = "1.0.79" 1115 source = "registry+https://github.com/rust-lang/crates.io-index" 1116 + checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 1117 dependencies = [ 1118 "unicode-ident", 1119 ] 1120 1121 [[package]] 1122 + name = "prometheus-client" 1123 + version = "0.22.2" 1124 source = "registry+https://github.com/rust-lang/crates.io-index" 1125 + checksum = "c1ca959da22a332509f2a73ae9e5f23f9dcfc31fd3a54d71f159495bd5909baa" 1126 + dependencies = [ 1127 + "dtoa", 1128 + "itoa", 1129 + "parking_lot", 1130 + "prometheus-client-derive-encode", 1131 + ] 1132 + 1133 + [[package]] 1134 + name = "prometheus-client-derive-encode" 1135 + version = "0.4.2" 1136 + source = "registry+https://github.com/rust-lang/crates.io-index" 1137 + checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" 1138 + dependencies = [ 1139 + "proc-macro2", 1140 + "quote", 1141 + "syn", 1142 + ] 1143 1144 [[package]] 1145 name = "quote" 1146 + version = "1.0.35" 1147 source = "registry+https://github.com/rust-lang/crates.io-index" 1148 + checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 1149 dependencies = [ 1150 "proc-macro2", 1151 ] ··· 1182 1183 [[package]] 1184 name = "redox_syscall" 1185 + version = "0.4.1" 1186 source = "registry+https://github.com/rust-lang/crates.io-index" 1187 + checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1188 dependencies = [ 1189 + "bitflags 1.3.2", 1190 ] 1191 1192 [[package]] 1193 name = "regex" 1194 + version = "1.10.3" 1195 source = "registry+https://github.com/rust-lang/crates.io-index" 1196 + checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 1197 + dependencies = [ 1198 + "aho-corasick", 1199 + "memchr", 1200 + "regex-automata", 1201 + "regex-syntax", 1202 + ] 1203 + 1204 + [[package]] 1205 + name = "regex-automata" 1206 + version = "0.4.6" 1207 + source = "registry+https://github.com/rust-lang/crates.io-index" 1208 + checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1209 dependencies = [ 1210 "aho-corasick", 1211 "memchr", ··· 1214 1215 [[package]] 1216 name = "regex-syntax" 1217 + version = "0.8.2" 1218 + source = "registry+https://github.com/rust-lang/crates.io-index" 1219 + checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 1220 + 1221 + [[package]] 1222 + name = "rpassword" 1223 + version = "7.3.1" 1224 + source = "registry+https://github.com/rust-lang/crates.io-index" 1225 + checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" 1226 + dependencies = [ 1227 + "libc", 1228 + "rtoolbox", 1229 + "windows-sys 0.48.0", 1230 + ] 1231 + 1232 + [[package]] 1233 + name = "rtoolbox" 1234 + version = "0.0.2" 1235 + source = "registry+https://github.com/rust-lang/crates.io-index" 1236 + checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" 1237 + dependencies = [ 1238 + "libc", 1239 + "windows-sys 0.48.0", 1240 + ] 1241 + 1242 + [[package]] 1243 + name = "rustc-demangle" 1244 + version = "0.1.23" 1245 source = "registry+https://github.com/rust-lang/crates.io-index" 1246 + checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1247 1248 [[package]] 1249 + name = "rustix" 1250 + version = "0.38.32" 1251 source = "registry+https://github.com/rust-lang/crates.io-index" 1252 + checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" 1253 dependencies = [ 1254 + "bitflags 2.5.0", 1255 + "errno", 1256 + "libc", 1257 + "linux-raw-sys", 1258 + "windows-sys 0.52.0", 1259 ] 1260 1261 [[package]] 1262 name = "rustls-pemfile" 1263 + version = "1.0.4" 1264 source = "registry+https://github.com/rust-lang/crates.io-index" 1265 + checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" 1266 dependencies = [ 1267 "base64", 1268 ] 1269 1270 [[package]] 1271 name = "ryu" 1272 + version = "1.0.17" 1273 source = "registry+https://github.com/rust-lang/crates.io-index" 1274 + checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 1275 1276 [[package]] 1277 + name = "schannel" 1278 + version = "0.1.23" 1279 source = "registry+https://github.com/rust-lang/crates.io-index" 1280 + checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 1281 + dependencies = [ 1282 + "windows-sys 0.52.0", 1283 + ] 1284 1285 [[package]] 1286 + name = "scoped-futures" 1287 + version = "0.1.3" 1288 source = "registry+https://github.com/rust-lang/crates.io-index" 1289 + checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" 1290 dependencies = [ 1291 + "cfg-if", 1292 + "pin-utils", 1293 ] 1294 1295 [[package]] 1296 name = "scoped-tls" 1297 + version = "1.0.1" 1298 source = "registry+https://github.com/rust-lang/crates.io-index" 1299 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 1300 1301 [[package]] 1302 name = "scopeguard" 1303 + version = "1.2.0" 1304 source = "registry+https://github.com/rust-lang/crates.io-index" 1305 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1306 1307 [[package]] 1308 name = "security-framework" 1309 + version = "2.9.2" 1310 source = "registry+https://github.com/rust-lang/crates.io-index" 1311 + checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" 1312 dependencies = [ 1313 + "bitflags 1.3.2", 1314 "core-foundation", 1315 "core-foundation-sys", 1316 "libc", ··· 1319 1320 [[package]] 1321 name = "security-framework-sys" 1322 + version = "2.9.1" 1323 source = "registry+https://github.com/rust-lang/crates.io-index" 1324 + checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" 1325 dependencies = [ 1326 "core-foundation-sys", 1327 "libc", ··· 1329 1330 [[package]] 1331 name = "serde" 1332 + version = "1.0.197" 1333 source = "registry+https://github.com/rust-lang/crates.io-index" 1334 + checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 1335 dependencies = [ 1336 "serde_derive", 1337 ] 1338 1339 [[package]] 1340 name = "serde_derive" 1341 + version = "1.0.197" 1342 source = "registry+https://github.com/rust-lang/crates.io-index" 1343 + checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 1344 dependencies = [ 1345 "proc-macro2", 1346 "quote", ··· 1349 1350 [[package]] 1351 name = "serde_json" 1352 + version = "1.0.114" 1353 source = "registry+https://github.com/rust-lang/crates.io-index" 1354 + checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" 1355 dependencies = [ 1356 "itoa", 1357 "ryu", ··· 1359 ] 1360 1361 [[package]] 1362 + name = "serde_spanned" 1363 + version = "0.6.5" 1364 + source = "registry+https://github.com/rust-lang/crates.io-index" 1365 + checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 1366 + dependencies = [ 1367 + "serde", 1368 + ] 1369 + 1370 + [[package]] 1371 name = "serde_urlencoded" 1372 version = "0.7.1" 1373 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1380 ] 1381 1382 [[package]] 1383 name = "sha1" 1384 + version = "0.10.6" 1385 source = "registry+https://github.com/rust-lang/crates.io-index" 1386 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1387 dependencies = [ 1388 "cfg-if", 1389 "cpufeatures", ··· 1392 1393 [[package]] 1394 name = "sha2" 1395 + version = "0.10.8" 1396 source = "registry+https://github.com/rust-lang/crates.io-index" 1397 + checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1398 dependencies = [ 1399 "cfg-if", 1400 "cpufeatures", ··· 1403 1404 [[package]] 1405 name = "signal-hook-registry" 1406 + version = "1.4.1" 1407 source = "registry+https://github.com/rust-lang/crates.io-index" 1408 + checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1409 dependencies = [ 1410 "libc", 1411 ] 1412 1413 [[package]] 1414 name = "siphasher" 1415 + version = "0.3.11" 1416 source = "registry+https://github.com/rust-lang/crates.io-index" 1417 + checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 1418 1419 [[package]] 1420 name = "slab" 1421 + version = "0.4.9" 1422 source = "registry+https://github.com/rust-lang/crates.io-index" 1423 + checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1424 dependencies = [ 1425 "autocfg", 1426 ] 1427 1428 [[package]] 1429 name = "smallvec" 1430 + version = "1.13.2" 1431 source = "registry+https://github.com/rust-lang/crates.io-index" 1432 + checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1433 1434 [[package]] 1435 name = "socket2" 1436 + version = "0.5.6" 1437 source = "registry+https://github.com/rust-lang/crates.io-index" 1438 + checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" 1439 dependencies = [ 1440 "libc", 1441 + "windows-sys 0.52.0", 1442 ] 1443 1444 [[package]] 1445 + name = "spin" 1446 + version = "0.9.8" 1447 + source = "registry+https://github.com/rust-lang/crates.io-index" 1448 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1449 + 1450 + [[package]] 1451 name = "stringprep" 1452 + version = "0.1.4" 1453 source = "registry+https://github.com/rust-lang/crates.io-index" 1454 + checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" 1455 dependencies = [ 1456 + "finl_unicode", 1457 "unicode-bidi", 1458 "unicode-normalization", 1459 ] 1460 1461 [[package]] 1462 name = "strsim" 1463 + version = "0.11.0" 1464 source = "registry+https://github.com/rust-lang/crates.io-index" 1465 + checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 1466 1467 [[package]] 1468 name = "subtle" 1469 + version = "2.5.0" 1470 source = "registry+https://github.com/rust-lang/crates.io-index" 1471 + checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 1472 1473 [[package]] 1474 name = "syn" 1475 + version = "2.0.53" 1476 source = "registry+https://github.com/rust-lang/crates.io-index" 1477 + checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" 1478 dependencies = [ 1479 "proc-macro2", 1480 "quote", ··· 1483 1484 [[package]] 1485 name = "tempfile" 1486 + version = "3.10.1" 1487 source = "registry+https://github.com/rust-lang/crates.io-index" 1488 + checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 1489 dependencies = [ 1490 "cfg-if", 1491 "fastrand", 1492 + "rustix", 1493 + "windows-sys 0.52.0", 1494 ] 1495 1496 [[package]] 1497 name = "termcolor" 1498 + version = "1.4.1" 1499 source = "registry+https://github.com/rust-lang/crates.io-index" 1500 + checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1501 dependencies = [ 1502 "winapi-util", 1503 ] 1504 1505 [[package]] 1506 name = "thiserror" 1507 + version = "1.0.58" 1508 source = "registry+https://github.com/rust-lang/crates.io-index" 1509 + checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 1510 dependencies = [ 1511 "thiserror-impl", 1512 ] 1513 1514 [[package]] 1515 name = "thiserror-impl" 1516 + version = "1.0.58" 1517 source = "registry+https://github.com/rust-lang/crates.io-index" 1518 + checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 1519 dependencies = [ 1520 "proc-macro2", 1521 "quote", ··· 1523 ] 1524 1525 [[package]] 1526 + name = "time" 1527 + version = "0.3.34" 1528 + source = "registry+https://github.com/rust-lang/crates.io-index" 1529 + checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" 1530 + dependencies = [ 1531 + "deranged", 1532 + "num-conv", 1533 + "powerfmt", 1534 + "serde", 1535 + "time-core", 1536 + "time-macros", 1537 + ] 1538 + 1539 + [[package]] 1540 + name = "time-core" 1541 + version = "0.1.2" 1542 + source = "registry+https://github.com/rust-lang/crates.io-index" 1543 + checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1544 + 1545 + [[package]] 1546 + name = "time-macros" 1547 + version = "0.2.17" 1548 + source = "registry+https://github.com/rust-lang/crates.io-index" 1549 + checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" 1550 + dependencies = [ 1551 + "num-conv", 1552 + "time-core", 1553 + ] 1554 + 1555 + [[package]] 1556 name = "tinyvec" 1557 version = "1.6.0" 1558 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1563 1564 [[package]] 1565 name = "tinyvec_macros" 1566 + version = "0.1.1" 1567 source = "registry+https://github.com/rust-lang/crates.io-index" 1568 + checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1569 1570 [[package]] 1571 name = "tokio" 1572 + version = "1.36.0" 1573 source = "registry+https://github.com/rust-lang/crates.io-index" 1574 + checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" 1575 dependencies = [ 1576 + "backtrace", 1577 "bytes", 1578 "libc", 1579 "mio", 1580 "num_cpus", 1581 "parking_lot", ··· 1583 "signal-hook-registry", 1584 "socket2", 1585 "tokio-macros", 1586 + "windows-sys 0.48.0", 1587 ] 1588 1589 [[package]] 1590 name = "tokio-macros" 1591 + version = "2.2.0" 1592 source = "registry+https://github.com/rust-lang/crates.io-index" 1593 + checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 1594 dependencies = [ 1595 "proc-macro2", 1596 "quote", ··· 1599 1600 [[package]] 1601 name = "tokio-native-tls" 1602 + version = "0.3.1" 1603 source = "registry+https://github.com/rust-lang/crates.io-index" 1604 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1605 dependencies = [ 1606 "native-tls", 1607 "tokio", ··· 1609 1610 [[package]] 1611 name = "tokio-postgres" 1612 + version = "0.7.10" 1613 source = "registry+https://github.com/rust-lang/crates.io-index" 1614 + checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" 1615 dependencies = [ 1616 "async-trait", 1617 "byteorder", ··· 1626 "pin-project-lite", 1627 "postgres-protocol", 1628 "postgres-types", 1629 + "rand", 1630 "socket2", 1631 "tokio", 1632 "tokio-util", 1633 + "whoami", 1634 ] 1635 1636 [[package]] 1637 name = "tokio-stream" 1638 + version = "0.1.15" 1639 source = "registry+https://github.com/rust-lang/crates.io-index" 1640 + checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 1641 dependencies = [ 1642 "futures-core", 1643 "pin-project-lite", ··· 1646 1647 [[package]] 1648 name = "tokio-tungstenite" 1649 + version = "0.20.1" 1650 source = "registry+https://github.com/rust-lang/crates.io-index" 1651 + checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" 1652 dependencies = [ 1653 "futures-util", 1654 "log", ··· 1658 1659 [[package]] 1660 name = "tokio-util" 1661 + version = "0.7.10" 1662 source = "registry+https://github.com/rust-lang/crates.io-index" 1663 + checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 1664 dependencies = [ 1665 "bytes", 1666 "futures-core", ··· 1672 1673 [[package]] 1674 name = "toml" 1675 + version = "0.8.12" 1676 source = "registry+https://github.com/rust-lang/crates.io-index" 1677 + checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 1678 dependencies = [ 1679 "serde", 1680 + "serde_spanned", 1681 + "toml_datetime", 1682 + "toml_edit", 1683 + ] 1684 + 1685 + [[package]] 1686 + name = "toml_datetime" 1687 + version = "0.6.5" 1688 + source = "registry+https://github.com/rust-lang/crates.io-index" 1689 + checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 1690 + dependencies = [ 1691 + "serde", 1692 + ] 1693 + 1694 + [[package]] 1695 + name = "toml_edit" 1696 + version = "0.22.9" 1697 + source = "registry+https://github.com/rust-lang/crates.io-index" 1698 + checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" 1699 + dependencies = [ 1700 + "indexmap", 1701 + "serde", 1702 + "serde_spanned", 1703 + "toml_datetime", 1704 + "winnow", 1705 ] 1706 1707 [[package]] ··· 1712 1713 [[package]] 1714 name = "tracing" 1715 + version = "0.1.40" 1716 source = "registry+https://github.com/rust-lang/crates.io-index" 1717 + checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1718 dependencies = [ 1719 "log", 1720 "pin-project-lite", 1721 "tracing-core", ··· 1723 1724 [[package]] 1725 name = "tracing-core" 1726 + version = "0.1.32" 1727 source = "registry+https://github.com/rust-lang/crates.io-index" 1728 + checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1729 dependencies = [ 1730 "once_cell", 1731 ] 1732 1733 [[package]] 1734 name = "try-lock" 1735 + version = "0.2.5" 1736 source = "registry+https://github.com/rust-lang/crates.io-index" 1737 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1738 1739 [[package]] 1740 name = "tungstenite" 1741 + version = "0.20.1" 1742 source = "registry+https://github.com/rust-lang/crates.io-index" 1743 + checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" 1744 dependencies = [ 1745 "byteorder", 1746 "bytes", 1747 + "data-encoding", 1748 "http", 1749 "httparse", 1750 "log", 1751 "rand", 1752 + "sha1", 1753 "thiserror", 1754 "url", 1755 "utf-8", 1756 ] 1757 1758 [[package]] 1759 name = "typenum" 1760 + version = "1.17.0" 1761 source = "registry+https://github.com/rust-lang/crates.io-index" 1762 + checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1763 1764 [[package]] 1765 name = "unicase" 1766 + version = "2.7.0" 1767 source = "registry+https://github.com/rust-lang/crates.io-index" 1768 + checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 1769 dependencies = [ 1770 "version_check", 1771 ] 1772 1773 [[package]] 1774 name = "unicode-bidi" 1775 + version = "0.3.15" 1776 source = "registry+https://github.com/rust-lang/crates.io-index" 1777 + checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1778 1779 [[package]] 1780 name = "unicode-ident" 1781 + version = "1.0.12" 1782 source = "registry+https://github.com/rust-lang/crates.io-index" 1783 + checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1784 1785 [[package]] 1786 name = "unicode-normalization" 1787 + version = "0.1.23" 1788 source = "registry+https://github.com/rust-lang/crates.io-index" 1789 + checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1790 dependencies = [ 1791 "tinyvec", 1792 ] 1793 1794 [[package]] 1795 name = "url" 1796 + version = "2.5.0" 1797 source = "registry+https://github.com/rust-lang/crates.io-index" 1798 + checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 1799 dependencies = [ 1800 "form_urlencoded", 1801 "idna", ··· 1809 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1810 1811 [[package]] 1812 + name = "utf8parse" 1813 + version = "0.2.1" 1814 + source = "registry+https://github.com/rust-lang/crates.io-index" 1815 + checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1816 + 1817 + [[package]] 1818 name = "vcpkg" 1819 version = "0.2.15" 1820 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1828 1829 [[package]] 1830 name = "want" 1831 + version = "0.3.1" 1832 source = "registry+https://github.com/rust-lang/crates.io-index" 1833 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1834 dependencies = [ 1835 "try-lock", 1836 ] 1837 1838 [[package]] 1839 name = "warp" 1840 + version = "0.3.6" 1841 source = "registry+https://github.com/rust-lang/crates.io-index" 1842 + checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" 1843 dependencies = [ 1844 "bytes", 1845 "futures-channel", ··· 1850 "log", 1851 "mime", 1852 "mime_guess", 1853 + "multer", 1854 "percent-encoding", 1855 "pin-project", 1856 "rustls-pemfile", ··· 1873 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1874 1875 [[package]] 1876 + name = "wasite" 1877 + version = "0.1.0" 1878 + source = "registry+https://github.com/rust-lang/crates.io-index" 1879 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 1880 + 1881 + [[package]] 1882 + name = "wasm-bindgen" 1883 + version = "0.2.92" 1884 + source = "registry+https://github.com/rust-lang/crates.io-index" 1885 + checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1886 + dependencies = [ 1887 + "cfg-if", 1888 + "wasm-bindgen-macro", 1889 + ] 1890 + 1891 + [[package]] 1892 + name = "wasm-bindgen-backend" 1893 + version = "0.2.92" 1894 + source = "registry+https://github.com/rust-lang/crates.io-index" 1895 + checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1896 + dependencies = [ 1897 + "bumpalo", 1898 + "log", 1899 + "once_cell", 1900 + "proc-macro2", 1901 + "quote", 1902 + "syn", 1903 + "wasm-bindgen-shared", 1904 + ] 1905 + 1906 + [[package]] 1907 + name = "wasm-bindgen-macro" 1908 + version = "0.2.92" 1909 + source = "registry+https://github.com/rust-lang/crates.io-index" 1910 + checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1911 + dependencies = [ 1912 + "quote", 1913 + "wasm-bindgen-macro-support", 1914 + ] 1915 + 1916 + [[package]] 1917 + name = "wasm-bindgen-macro-support" 1918 + version = "0.2.92" 1919 + source = "registry+https://github.com/rust-lang/crates.io-index" 1920 + checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1921 + dependencies = [ 1922 + "proc-macro2", 1923 + "quote", 1924 + "syn", 1925 + "wasm-bindgen-backend", 1926 + "wasm-bindgen-shared", 1927 + ] 1928 + 1929 + [[package]] 1930 + name = "wasm-bindgen-shared" 1931 + version = "0.2.92" 1932 + source = "registry+https://github.com/rust-lang/crates.io-index" 1933 + checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1934 + 1935 + [[package]] 1936 + name = "web-sys" 1937 + version = "0.3.69" 1938 + source = "registry+https://github.com/rust-lang/crates.io-index" 1939 + checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 1940 + dependencies = [ 1941 + "js-sys", 1942 + "wasm-bindgen", 1943 + ] 1944 + 1945 + [[package]] 1946 + name = "whoami" 1947 + version = "1.5.1" 1948 + source = "registry+https://github.com/rust-lang/crates.io-index" 1949 + checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" 1950 + dependencies = [ 1951 + "redox_syscall", 1952 + "wasite", 1953 + "web-sys", 1954 + ] 1955 + 1956 + [[package]] 1957 name = "winapi" 1958 version = "0.3.9" 1959 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1971 1972 [[package]] 1973 name = "winapi-util" 1974 + version = "0.1.6" 1975 source = "registry+https://github.com/rust-lang/crates.io-index" 1976 + checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 1977 dependencies = [ 1978 "winapi", 1979 ] ··· 1986 1987 [[package]] 1988 name = "windows-sys" 1989 + version = "0.48.0" 1990 + source = "registry+https://github.com/rust-lang/crates.io-index" 1991 + checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1992 + dependencies = [ 1993 + "windows-targets 0.48.5", 1994 + ] 1995 + 1996 + [[package]] 1997 + name = "windows-sys" 1998 + version = "0.52.0" 1999 + source = "registry+https://github.com/rust-lang/crates.io-index" 2000 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2001 + dependencies = [ 2002 + "windows-targets 0.52.4", 2003 + ] 2004 + 2005 + [[package]] 2006 + name = "windows-targets" 2007 + version = "0.48.5" 2008 + source = "registry+https://github.com/rust-lang/crates.io-index" 2009 + checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2010 + dependencies = [ 2011 + "windows_aarch64_gnullvm 0.48.5", 2012 + "windows_aarch64_msvc 0.48.5", 2013 + "windows_i686_gnu 0.48.5", 2014 + "windows_i686_msvc 0.48.5", 2015 + "windows_x86_64_gnu 0.48.5", 2016 + "windows_x86_64_gnullvm 0.48.5", 2017 + "windows_x86_64_msvc 0.48.5", 2018 + ] 2019 + 2020 + [[package]] 2021 + name = "windows-targets" 2022 + version = "0.52.4" 2023 source = "registry+https://github.com/rust-lang/crates.io-index" 2024 + checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 2025 dependencies = [ 2026 + "windows_aarch64_gnullvm 0.52.4", 2027 + "windows_aarch64_msvc 0.52.4", 2028 + "windows_i686_gnu 0.52.4", 2029 + "windows_i686_msvc 0.52.4", 2030 + "windows_x86_64_gnu 0.52.4", 2031 + "windows_x86_64_gnullvm 0.52.4", 2032 + "windows_x86_64_msvc 0.52.4", 2033 ] 2034 2035 [[package]] 2036 + name = "windows_aarch64_gnullvm" 2037 + version = "0.48.5" 2038 + source = "registry+https://github.com/rust-lang/crates.io-index" 2039 + checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2040 + 2041 + [[package]] 2042 + name = "windows_aarch64_gnullvm" 2043 + version = "0.52.4" 2044 + source = "registry+https://github.com/rust-lang/crates.io-index" 2045 + checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 2046 + 2047 + [[package]] 2048 name = "windows_aarch64_msvc" 2049 + version = "0.48.5" 2050 source = "registry+https://github.com/rust-lang/crates.io-index" 2051 + checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2052 + 2053 + [[package]] 2054 + name = "windows_aarch64_msvc" 2055 + version = "0.52.4" 2056 + source = "registry+https://github.com/rust-lang/crates.io-index" 2057 + checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 2058 2059 [[package]] 2060 name = "windows_i686_gnu" 2061 + version = "0.48.5" 2062 source = "registry+https://github.com/rust-lang/crates.io-index" 2063 + checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2064 + 2065 + [[package]] 2066 + name = "windows_i686_gnu" 2067 + version = "0.52.4" 2068 + source = "registry+https://github.com/rust-lang/crates.io-index" 2069 + checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 2070 2071 [[package]] 2072 name = "windows_i686_msvc" 2073 + version = "0.48.5" 2074 + source = "registry+https://github.com/rust-lang/crates.io-index" 2075 + checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2076 + 2077 + [[package]] 2078 + name = "windows_i686_msvc" 2079 + version = "0.52.4" 2080 + source = "registry+https://github.com/rust-lang/crates.io-index" 2081 + checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 2082 + 2083 + [[package]] 2084 + name = "windows_x86_64_gnu" 2085 + version = "0.48.5" 2086 source = "registry+https://github.com/rust-lang/crates.io-index" 2087 + checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2088 2089 [[package]] 2090 name = "windows_x86_64_gnu" 2091 + version = "0.52.4" 2092 + source = "registry+https://github.com/rust-lang/crates.io-index" 2093 + checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 2094 + 2095 + [[package]] 2096 + name = "windows_x86_64_gnullvm" 2097 + version = "0.48.5" 2098 + source = "registry+https://github.com/rust-lang/crates.io-index" 2099 + checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2100 + 2101 + [[package]] 2102 + name = "windows_x86_64_gnullvm" 2103 + version = "0.52.4" 2104 + source = "registry+https://github.com/rust-lang/crates.io-index" 2105 + checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 2106 + 2107 + [[package]] 2108 + name = "windows_x86_64_msvc" 2109 + version = "0.48.5" 2110 source = "registry+https://github.com/rust-lang/crates.io-index" 2111 + checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2112 2113 [[package]] 2114 name = "windows_x86_64_msvc" 2115 + version = "0.52.4" 2116 + source = "registry+https://github.com/rust-lang/crates.io-index" 2117 + checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 2118 + 2119 + [[package]] 2120 + name = "winnow" 2121 + version = "0.6.5" 2122 source = "registry+https://github.com/rust-lang/crates.io-index" 2123 + checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" 2124 + dependencies = [ 2125 + "memchr", 2126 + ]
+1
Cargo.toml
··· 1 [workspace] 2 members = [ 3 "common", 4 "lumen",
··· 1 [workspace] 2 + resolver = "2" 3 members = [ 4 "common", 5 "lumen",
+21 -12
Dockerfile
··· 1 - FROM debian:buster-slim 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 9 COPY common /lumen/common 10 COPY lumen /lumen/lumen 11 COPY Cargo.toml /lumen/ 12 - RUN cd /lumen && cargo build --release 13 14 FROM debian:buster-slim 15 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 19 COPY config-example.toml docker-init.sh /lumen/ 20 - RUN chmod ug+x /lumen/docker-init.sh && chmod ug+x /usr/bin/lumen 21 WORKDIR /lumen
··· 1 + FROM rust:1.74.1-slim-buster 2 ARG DEBIAN_FRONTEND=noninteractive 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.1.1 --no-default-features --features postgres 8 + 9 COPY common /lumen/common 10 COPY lumen /lumen/lumen 11 COPY Cargo.toml /lumen/ 12 + RUN --mount=type=cache,target=$CARGO_HOME/registry,target=/lumen/target \ 13 + cd /lumen && cargo build --release && cp /lumen/target/release/lumen /root/ 14 15 FROM debian:buster-slim 16 ARG DEBIAN_FRONTEND=noninteractive 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 + 26 COPY config-example.toml docker-init.sh /lumen/ 27 + RUN chmod a+x /lumen/docker-init.sh && chmod a+x /usr/bin/lumen 28 WORKDIR /lumen 29 + STOPSIGNAL SIGINT 30 + 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.
+14 -5
common/Cargo.toml
··· 3 description = "common utilities for lumen" 4 version = "0.2.0" 5 authors = ["Naim A. <naim@abda.nl>"] 6 - edition = "2018" 7 publish = false 8 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} 16 futures-util = "0.3" 17 - toml = "0.5" 18 warp = {version = "0.3", optional = true} 19 binascii = "0.1" 20 21 [features] 22 default = ["web", "db"] 23 web = ["warp"] 24 - db = ["tokio", "tokio-postgres", "postgres-native-tls", "native-tls"]
··· 3 description = "common utilities for lumen" 4 version = "0.2.0" 5 authors = ["Naim A. <naim@abda.nl>"] 6 + edition = "2021" 7 publish = false 8 9 [dependencies] 10 + tokio = {version = "1.32", 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} 15 futures-util = "0.3" 16 + toml = "0.8" 17 warp = {version = "0.3", optional = true} 18 binascii = "0.1" 19 20 + tokio-postgres = {version = "0.7", default-features = false, optional = true} 21 + diesel = {version = "2.1", optional = true, default-features = false, features = ["postgres_backend", "time"]} 22 + time = {version = "0.3.31", optional = true} 23 + diesel-async = {version = "0.4.1", optional = true, features = ["postgres", "bb8"]} 24 + anyhow = "1.0" 25 + prometheus-client = "0.22.0" 26 + pbkdf2 = { version = "0.12.2" } 27 + sha2 = "0.10.8" 28 + rand = "0.8.5" 29 + 30 [features] 31 default = ["web", "db"] 32 web = ["warp"] 33 + db = ["tokio", "postgres-native-tls", "native-tls", "diesel", "diesel-async", "tokio-postgres", "time"]
+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);
+21
common/migrations/2024-01-20-215809_users/down.sql
···
··· 1 + -- don't allow table to be modified until we're done... 2 + LOCK TABLE users; 3 + 4 + -- delete funcs that belong to users 5 + DELETE FROM funcs USING dbs, users 6 + WHERE dbs.id=funcs.db_id 7 + AND users.id=dbs.user_id 8 + AND users.cred_id IS NOT NULL; 9 + 10 + -- delete dbs that belong to users 11 + DELETE FROM dbs USING users WHERE dbs.user_id=users.id AND users.cred_id IS NOT NULL; 12 + 13 + -- delete all users with creds... 14 + DELETE FROM users WHERE cred_id is NOT NULL; 15 + DROP TABLE creds CASCADE; 16 + 17 + CREATE UNIQUE INDEX IF NOT EXISTS user_rec ON users(lic_id,lic_data,hostname); 18 + CREATE UNIQUE INDEX IF NOT EXISTS user_hn_null ON users (lic_id,lic_data, (hostname IS NULL)) WHERE hostname is NULL; 19 + DROP INDEX user_cred_idx; 20 + 21 + ALTER TABLE users DROP COLUMN cred_id;
+22
common/migrations/2024-01-20-215809_users/up.sql
···
··· 1 + CREATE TABLE creds ( 2 + id SERIAL PRIMARY KEY, 3 + 4 + username VARCHAR(256) UNIQUE NOT NULL, 5 + email VARCHAR(256) UNIQUE NOT NULL, 6 + 7 + passwd_salt bytea, 8 + passwd_iters INTEGER NOT NULL DEFAULT 10000, 9 + passwd_hash bytea, 10 + 11 + last_active TIMESTAMPTZ, 12 + creation_dt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, 13 + 14 + is_admin BOOLEAN NOT NULL DEFAULT FALSE, 15 + is_enabled BOOLEAN NOT NULL DEFAULT TRUE 16 + ); 17 + 18 + ALTER TABLE users ADD COLUMN cred_id INTEGER REFERENCES creds(id) ON DELETE CASCADE; 19 + 20 + CREATE UNIQUE INDEX user_cred_idx ON users(lic_id,lic_data,hostname,cred_id) NULLS NOT DISTINCT; 21 + DROP INDEX user_hn_null; 22 + DROP INDEX user_rec;
+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 + }
+125 -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::num::NonZeroU32; 3 + use std::time::Duration; 4 + use std::{net::SocketAddr, path::PathBuf}; 5 + use toml::from_str; 6 + 7 + #[derive(Deserialize)] 8 + pub struct TlsIdentity { 9 + pub server_cert: PathBuf, 10 + } 11 + 12 + #[derive(Deserialize)] 13 + #[serde(default)] 14 + pub struct LuminaServer { 15 + pub bind_addr: SocketAddr, 16 + pub use_tls: bool, 17 + pub tls: Option<TlsIdentity>, 18 + pub server_name: Option<String>, 19 + pub allow_deletes: bool, 20 + 21 + /// limit of function histories to return per function. 22 + /// `None`, or `Some(0)` will disable the feature on the server. 23 + pub get_history_limit: NonZeroU32, 24 + } 25 + impl Default for LuminaServer { 26 + fn default() -> Self { 27 + Self { 28 + bind_addr: "0.0.0.0:1234".parse().unwrap(), 29 + use_tls: false, 30 + tls: None, 31 + server_name: None, 32 + allow_deletes: false, 33 + get_history_limit: NonZeroU32::new(50).unwrap(), 34 + } 35 + } 36 + } 37 + 38 + #[derive(Deserialize)] 39 + pub struct WebServer { 40 + pub bind_addr: SocketAddr, 41 + } 42 + 43 + #[derive(Deserialize)] 44 + pub struct Database { 45 + pub connection_info: String, 46 + 47 + pub use_tls: bool, 48 + pub server_ca: Option<PathBuf>, 49 + pub client_id: Option<PathBuf>, 50 + } 51 + 52 + #[derive(Deserialize, Debug)] 53 + #[serde(default)] 54 + pub struct Limits { 55 + /// Maximum time to wait on an idle connection between commands. 56 + pub command_timeout: Duration, 57 + 58 + /// Maximum time to all `PULL_MD` queries. 59 + pub pull_md_timeout: Duration, 60 + 61 + /// Maximum time to wait for `HELO` message. 62 + pub hello_timeout: Duration, 63 + 64 + /// Maximum time allowed until TLS handshake completes. 65 + pub tls_handshake_timeout: Duration, 66 + } 67 + 68 + impl Default for Limits { 69 + fn default() -> Self { 70 + Self { 71 + command_timeout: Duration::from_secs(3600), 72 + pull_md_timeout: Duration::from_secs(4 * 60), 73 + hello_timeout: Duration::from_secs(15), 74 + tls_handshake_timeout: Duration::from_secs(10), 75 + } 76 + } 77 + } 78 + 79 + #[derive(Deserialize)] 80 + #[serde(default)] 81 + pub struct Users { 82 + /// Sets if guests are allowed to login. required for IDA<8.1 83 + pub allow_guests: bool, 84 + 85 + /// PBKDF2 iterations for newly set passwords. 86 + pub pbkdf2_iterations: NonZeroU32, 87 + } 88 + 89 + impl Default for Users { 90 + fn default() -> Self { 91 + Self { allow_guests: true, pbkdf2_iterations: NonZeroU32::new(120_000).unwrap() } 92 + } 93 + } 94 + 95 + #[derive(Deserialize)] 96 + pub struct Config { 97 + pub lumina: LuminaServer, 98 + pub api_server: Option<WebServer>, 99 + pub database: Database, 100 + 101 + #[serde(default)] 102 + pub limits: Limits, 103 + 104 + #[serde(default)] 105 + pub users: Users, 106 + } 107 + 108 + pub trait HasConfig { 109 + fn get_config(&self) -> &Config; 110 + } 111 + 112 + impl HasConfig for Config { 113 + fn get_config(&self) -> &Config { 114 + self 115 + } 116 + } 117 + 118 + pub fn load_config<R: std::io::Read>(mut fd: R) -> Config { 119 + let mut buf = vec![]; 120 + fd.read_to_end(&mut buf).expect("failed to read config"); 121 + 122 + let buf = std::str::from_utf8(&buf).expect("file contains invalid utf-8"); 123 + 124 + from_str(buf).expect("failed to parse configuration") 125 + }
+592 -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::{ 2 + async_drop::{AsyncDropGuard, AsyncDropper}, 3 + db::schema::Creds, 4 + }; 5 + use log::*; 6 + use postgres_native_tls::MakeTlsConnector; 7 + use serde::Serialize; 8 + use std::collections::HashMap; 9 + use time::OffsetDateTime; 10 + use tokio_postgres::{tls::MakeTlsConnect, NoTls, Socket}; 11 + pub mod schema; 12 + mod schema_auto; 13 + 14 + use diesel::{ 15 + query_builder::{Query, QueryFragment}, 16 + sql_types::{Array, Binary, Integer, VarChar}, 17 + upsert::excluded, 18 + ExpressionMethods, NullableExpressionMethods, QueryDsl, SelectableHelper, 19 + }; 20 + use diesel_async::{pooled_connection::ManagerConfig, RunQueryDsl}; 21 + 22 + use self::schema::creds; 23 + 24 + pub type DynConfig = dyn crate::config::HasConfig + Send + Sync; 25 + 26 + pub struct Database { 27 + tls_connector: Option<MakeTlsConnector>, 28 + diesel: diesel_async::pooled_connection::bb8::Pool<diesel_async::AsyncPgConnection>, 29 + dropper: AsyncDropper, 30 + } 31 + 32 + pub struct FunctionInfo { 33 + pub name: String, 34 + pub len: u32, 35 + pub data: Vec<u8>, 36 + pub popularity: u32, 37 + } 38 + 39 + #[derive(Debug, Serialize)] 40 + pub struct DbStats { 41 + unique_lics: i32, 42 + unique_hosts_per_lic: i32, 43 + 44 + unique_funcs: i32, 45 + total_funcs: i32, 46 + 47 + dbs: i32, 48 + unique_files: i32, 49 + } 50 + 51 + impl Database { 52 + pub async fn open(config: &crate::config::Database) -> Result<Self, anyhow::Error> { 53 + let connection_string = config.connection_info.as_str(); 54 + let tls_connector = if config.use_tls { Some(Self::make_tls(config).await) } else { None }; 55 + 56 + let (dropper, worker) = AsyncDropper::new(); 57 + tokio::task::spawn(worker); 58 + 59 + let diesel = Self::make_bb8_pool(connection_string, tls_connector.clone()).await?; 60 + 61 + Ok(Database { tls_connector, dropper, diesel }) 62 + } 63 + 64 + async fn make_pg_client<T>( 65 + db_url: &str, tls: T, 66 + ) -> diesel::result::ConnectionResult<diesel_async::AsyncPgConnection> 67 + where 68 + T: MakeTlsConnect<Socket>, 69 + T::Stream: Send + 'static, 70 + { 71 + let (cli, conn) = tokio_postgres::connect(db_url, tls).await.map_err(|e| { 72 + error!("failed to connect db: {e}"); 73 + diesel::result::ConnectionError::BadConnection(format!("{e}")) 74 + })?; 75 + 76 + tokio::spawn(async move { 77 + if let Err(e) = conn.await { 78 + error!("connection task error: {e}"); 79 + } 80 + }); 81 + 82 + diesel_async::AsyncPgConnection::try_from(cli).await 83 + } 84 + 85 + async fn make_bb8_pool( 86 + db_url: &str, tls: Option<MakeTlsConnector>, 87 + ) -> Result< 88 + diesel_async::pooled_connection::bb8::Pool<diesel_async::AsyncPgConnection>, 89 + anyhow::Error, 90 + > { 91 + let mut config = ManagerConfig::default(); 92 + config.custom_setup = Box::new(move |db_url| { 93 + let tls = tls.clone(); 94 + Box::pin(async move { 95 + if let Some(tls) = tls { 96 + Self::make_pg_client(db_url, tls).await 97 + } else { 98 + Self::make_pg_client(db_url, NoTls).await 99 + } 100 + }) 101 + }); 102 + let cfg = diesel_async::pooled_connection::AsyncDieselConnectionManager::< 103 + diesel_async::AsyncPgConnection, 104 + >::new_with_config(db_url, config); 105 + 106 + let pool = diesel_async::pooled_connection::bb8::Pool::builder() 107 + .min_idle(Some(1)) 108 + .build(cfg) 109 + .await?; 110 + Ok(pool) 111 + } 112 + 113 + async fn make_tls(database: &crate::config::Database) -> MakeTlsConnector { 114 + use native_tls::{Certificate, Identity, TlsConnector}; 115 + 116 + let mut tls_connector = TlsConnector::builder(); 117 + 118 + if let Some(ref client_identity) = database.client_id { 119 + let client_identity = 120 + tokio::fs::read(client_identity).await.expect("failed to read db's client id"); 121 + let client_identity = Identity::from_pkcs12(&client_identity, "") 122 + .expect("failed to load db's client identity (PKCS12)"); 123 + tls_connector.identity(client_identity); 124 + } 125 + 126 + if let Some(ref server_ca) = database.server_ca { 127 + let server_ca = 128 + tokio::fs::read(server_ca).await.expect("failed to read db's server ca"); 129 + let server_ca = 130 + Certificate::from_pem(&server_ca).expect("failed to load db's server ca (PEM)"); 131 + tls_connector.add_root_certificate(server_ca); 132 + } 133 + 134 + let tls_connector = tls_connector 135 + .danger_accept_invalid_hostnames(true) 136 + .build() 137 + .expect("failed to build TlsConnector"); 138 + 139 + MakeTlsConnector::new(tls_connector) 140 + } 141 + 142 + pub async fn get_funcs( 143 + &self, funcs: &[crate::rpc::PatternId<'_>], 144 + ) -> Result<Vec<Option<FunctionInfo>>, anyhow::Error> { 145 + let chksums: Vec<&[u8]> = funcs.iter().map(|v| v.data).collect(); 146 + 147 + let rows: Vec<(String, i32, Vec<u8>, Vec<u8>)> = { 148 + let conn = &mut self.diesel.get().await?; 149 + 150 + let ct = self.cancel_guard(&*conn); 151 + 152 + let res: Vec<_> = BestMds(chksums.as_slice()).get_results::<_>(conn).await?; 153 + ct.consume(); 154 + res 155 + }; 156 + 157 + let mut partial: HashMap<Vec<u8>, FunctionInfo> = rows 158 + .into_iter() 159 + .map(|row| { 160 + let v = FunctionInfo { name: row.0, len: row.1 as u32, data: row.2, popularity: 0 }; 161 + 162 + (row.3, v) 163 + }) 164 + .collect(); 165 + 166 + let results = partial.len(); 167 + 168 + let res: Vec<Option<FunctionInfo>> = 169 + chksums.iter().map(|&chksum| partial.remove(chksum)).collect(); 170 + 171 + trace!("found {}/{} results", results, chksums.len()); 172 + debug_assert_eq!(chksums.len(), res.len()); 173 + Ok(res) 174 + } 175 + 176 + pub async fn get_or_create_user<'a>( 177 + &self, user: &'a crate::rpc::RpcHello<'a>, cred_id: Option<i32>, hostname: &str, 178 + ) -> Result<i32, anyhow::Error> { 179 + use schema::users; 180 + 181 + let conn = &mut self.diesel.get().await?; 182 + 183 + let lic_id = &user.lic_number[..]; 184 + let lic_data = user.license_data; 185 + 186 + let get_user = || { 187 + let query = users::table 188 + .into_boxed() 189 + .select(users::id) 190 + .filter(users::lic_data.eq(lic_data)) 191 + .filter(users::lic_id.eq(lic_id)) 192 + .filter(users::hostname.eq(hostname)); 193 + 194 + if let Some(cred_id) = cred_id { 195 + query.filter(users::cred_id.eq(cred_id)) 196 + } else { 197 + query.filter(users::cred_id.is_null()) 198 + } 199 + }; 200 + 201 + match get_user().get_result::<i32>(conn).await { 202 + Ok(v) => return Ok(v), 203 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 204 + _ => {}, 205 + }; 206 + 207 + match diesel::insert_into(users::table) 208 + .values(vec![( 209 + users::lic_id.eq(lic_id), 210 + users::lic_data.eq(lic_data), 211 + users::hostname.eq(hostname), 212 + users::cred_id.eq(cred_id), 213 + )]) 214 + .returning(users::id) // xmax = 0 if the row is new 215 + .get_result::<i32>(conn) 216 + .await 217 + { 218 + Ok(v) => return Ok(v), 219 + Err(diesel::result::Error::DatabaseError( 220 + diesel::result::DatabaseErrorKind::UniqueViolation, 221 + _, 222 + )) => {}, 223 + Err(e) => return Err(e.into()), 224 + } 225 + 226 + Ok(get_user().get_result::<i32>(conn).await?) 227 + } 228 + 229 + async fn get_or_create_file<'a>( 230 + &self, funcs: &'a crate::rpc::PushMetadata<'a>, 231 + ) -> Result<i32, anyhow::Error> { 232 + use schema::files::{chksum, id, table as files}; 233 + 234 + let hash = &funcs.input_md5[..]; 235 + 236 + let conn = &mut self.diesel.get().await?; 237 + 238 + let get_file = || files.filter(chksum.eq(hash)).select(id); 239 + 240 + match get_file().get_result::<i32>(conn).await { 241 + Ok(v) => return Ok(v), 242 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 243 + _ => {}, 244 + } 245 + 246 + match diesel::insert_into(files) 247 + .values(vec![(chksum.eq(hash),)]) 248 + .returning(id) 249 + .get_result::<i32>(conn) 250 + .await 251 + { 252 + Ok(v) => return Ok(v), 253 + Err(diesel::result::Error::DatabaseError( 254 + diesel::result::DatabaseErrorKind::UniqueViolation, 255 + _, 256 + )) => {}, 257 + Err(e) => return Err(e.into()), 258 + } 259 + Ok(get_file().get_result::<i32>(conn).await?) 260 + } 261 + 262 + async fn get_or_create_db<'a>( 263 + &self, user: &'a crate::rpc::RpcHello<'a>, cred_id: Option<i32>, 264 + funcs: &'a crate::rpc::PushMetadata<'a>, 265 + ) -> Result<i32, anyhow::Error> { 266 + use schema::dbs::{ 267 + file_id as db_file_id, file_path, id as db_id, idb_path, table as dbs, 268 + user_id as db_user, 269 + }; 270 + 271 + let file_id = self.get_or_create_file(funcs); 272 + let user_id = self.get_or_create_user(user, cred_id, funcs.hostname); 273 + 274 + let (file_id, user_id): (i32, i32) = futures_util::try_join!(file_id, user_id)?; 275 + 276 + let conn = &mut self.diesel.get().await?; 277 + 278 + let get_db = || { 279 + dbs.select(db_id) 280 + .filter(db_user.eq(user_id)) 281 + .filter(db_file_id.eq(file_id)) 282 + .filter(file_path.eq(funcs.input_path)) 283 + .filter(idb_path.eq(funcs.idb_path)) 284 + }; 285 + 286 + match get_db().get_result::<i32>(conn).await { 287 + Ok(v) => return Ok(v), 288 + Err(err) if err != diesel::result::Error::NotFound => return Err(err.into()), 289 + _ => {}, 290 + }; 291 + 292 + match diesel::insert_into(dbs) 293 + .values(vec![( 294 + db_user.eq(user_id), 295 + db_file_id.eq(file_id), 296 + file_path.eq(funcs.input_path), 297 + idb_path.eq(funcs.idb_path), 298 + )]) 299 + .returning(db_id) 300 + .get_result::<i32>(conn) 301 + .await 302 + { 303 + Ok(id) => return Ok(id), 304 + Err(diesel::result::Error::DatabaseError( 305 + diesel::result::DatabaseErrorKind::UniqueViolation, 306 + _, 307 + )) => {}, 308 + Err(e) => return Err(e.into()), 309 + }; 310 + Ok(get_db().get_result::<i32>(conn).await?) 311 + } 312 + 313 + pub async fn push_funcs<'a, 'b>( 314 + &'b self, user: &'a crate::rpc::RpcHello<'a>, cred_id: Option<i32>, 315 + funcs: &'a crate::rpc::PushMetadata<'a>, scores: &[u32], 316 + ) -> Result<Vec<bool>, anyhow::Error> { 317 + use futures_util::TryStreamExt; 318 + 319 + // postgres has a limitation of binding per statement (i16::MAX). Split large push requests into smaller chunks. 320 + const PUSH_FUNC_CHUNK_SIZE: usize = 3000; 321 + 322 + let db_id = self.get_or_create_db(user, cred_id, funcs).await?; 323 + 324 + let mut rows = Vec::with_capacity(funcs.funcs.len().min(PUSH_FUNC_CHUNK_SIZE)); 325 + let mut is_new = Vec::with_capacity(funcs.funcs.len()); 326 + let conn = &mut self.diesel.get().await?; 327 + let f2 = diesel::alias!(schema::funcs as f2); 328 + 329 + for (idx, (func, &score)) in funcs.funcs.iter().zip(scores.iter()).enumerate() { 330 + let name = func.name; 331 + let len = func.func_len as i32; 332 + let chksum = func.pattern_id.data; 333 + let md = func.func_data; 334 + let score = score as i32; 335 + 336 + rows.push(( 337 + schema::funcs::name.eq(name), 338 + schema::funcs::len.eq(len), 339 + schema::funcs::chksum.eq(chksum), 340 + schema::funcs::metadata.eq(md), 341 + schema::funcs::rank.eq(score), 342 + schema::funcs::db_id.eq(db_id), 343 + )); 344 + 345 + if rows.len() < PUSH_FUNC_CHUNK_SIZE && idx < funcs.funcs.len() - 1 { 346 + continue; 347 + } 348 + 349 + let mut current_rows = 350 + Vec::with_capacity((funcs.funcs.len() - (idx + 1)).max(PUSH_FUNC_CHUNK_SIZE)); 351 + std::mem::swap(&mut current_rows, &mut rows); 352 + 353 + diesel::insert_into(schema::funcs::table) 354 + .values(current_rows) 355 + .on_conflict((schema::funcs::chksum, schema::funcs::db_id)) 356 + .do_update() 357 + .set(( 358 + schema::funcs::name.eq(excluded(schema::funcs::name)), 359 + schema::funcs::metadata.eq(excluded(schema::funcs::metadata)), 360 + schema::funcs::rank.eq(excluded(schema::funcs::rank)), 361 + schema::funcs::update_dt.eq(diesel::dsl::now), 362 + )) 363 + .returning(diesel::dsl::not(diesel::dsl::exists( 364 + f2.filter(f2.field(schema::funcs::chksum).eq(schema::funcs::chksum)), 365 + ))) // xmax=0 when a new row is created. 366 + .load_stream::<bool>(conn) 367 + .await? 368 + .try_fold(&mut is_new, |acc, item: bool| { 369 + acc.push(item); 370 + futures_util::future::ready(Ok(acc)) 371 + }) 372 + .await?; 373 + } 374 + 375 + Ok(is_new) 376 + } 377 + 378 + pub async fn get_file_funcs( 379 + &self, md5: &[u8], offset: i64, limit: i64, 380 + ) -> Result<Vec<(String, i32, Vec<u8>)>, anyhow::Error> { 381 + let conn = &mut self.diesel.get().await?; 382 + let results = schema::funcs::table 383 + .left_join(schema::dbs::table.left_join(schema::files::table)) 384 + .select(( 385 + schema::funcs::name.assume_not_null(), 386 + schema::funcs::len.assume_not_null(), 387 + schema::funcs::chksum.assume_not_null(), 388 + )) 389 + .filter(schema::files::chksum.eq(md5)) 390 + .offset(offset) 391 + .limit(limit) 392 + .get_results::<(String, i32, Vec<u8>)>(conn) 393 + .await?; 394 + Ok(results) 395 + } 396 + 397 + pub async fn get_files_with_func(&self, func: &[u8]) -> Result<Vec<Vec<u8>>, anyhow::Error> { 398 + let conn = &mut self.diesel.get().await?; 399 + 400 + let res = schema::files::table 401 + .left_join(schema::dbs::table.left_join(schema::funcs::table)) 402 + .select(schema::files::chksum.assume_not_null()) 403 + .distinct() 404 + .filter(schema::funcs::chksum.eq(func)) 405 + .get_results::<Vec<u8>>(conn) 406 + .await?; 407 + Ok(res) 408 + } 409 + 410 + fn cancel_guard( 411 + &self, 412 + conn: &diesel_async::pooled_connection::bb8::PooledConnection< 413 + '_, 414 + diesel_async::AsyncPgConnection, 415 + >, 416 + ) -> AsyncDropGuard { 417 + let token = conn.cancel_token(); 418 + let tls_connector = self.tls_connector.clone(); 419 + self.dropper.defer(async move { 420 + debug!("cancelling query..."); 421 + 422 + if let Some(tls) = tls_connector { 423 + let _ = token.cancel_query(tls).await; 424 + } else { 425 + let _ = token.cancel_query(NoTls).await; 426 + } 427 + }) 428 + } 429 + 430 + pub async fn delete_metadata( 431 + &self, req: &crate::rpc::DelHistory<'_>, 432 + ) -> Result<(), anyhow::Error> { 433 + use schema::funcs::{chksum, table as funcs}; 434 + 435 + let chksums = req.calcrel_hashes.iter().map(|v| v.as_slice()).collect::<Vec<_>>(); 436 + 437 + let conn = &mut self.diesel.get().await?; 438 + let rows_modified = 439 + diesel::delete(funcs.filter(chksum.eq_any(&chksums))).execute(conn).await?; 440 + 441 + debug!("deleted {rows_modified} rows"); 442 + 443 + Ok(()) 444 + } 445 + 446 + pub async fn get_func_histories( 447 + &self, chksum: &[u8], limit: u32, 448 + ) -> Result<Vec<(OffsetDateTime, String, Vec<u8>)>, anyhow::Error> { 449 + let conn = &mut self.diesel.get().await?; 450 + let rows = &schema::funcs::table.select(( 451 + schema::funcs::update_dt.assume_not_null(), 452 + schema::funcs::name, 453 + schema::funcs::metadata.assume_not_null(), 454 + )); 455 + let rows = rows 456 + .limit(limit as i64) 457 + .order_by(schema::funcs::update_dt.desc()) 458 + .filter(schema::funcs::chksum.eq(chksum)) 459 + .get_results::<(time::OffsetDateTime, String, Vec<u8>)>(conn) 460 + .await?; 461 + Ok(rows) 462 + } 463 + 464 + pub async fn get_users(&self) -> Result<Vec<(i32, String, String, bool, bool)>, anyhow::Error> { 465 + let db = &mut self.diesel.get().await?; 466 + 467 + creds::table 468 + .select(( 469 + creds::id, 470 + creds::username, 471 + creds::email, 472 + creds::is_admin, 473 + creds::passwd_hash.is_not_null(), 474 + )) 475 + .get_results::<(i32, String, String, bool, bool)>(db) 476 + .await 477 + .map_err(|v| v.into()) 478 + } 479 + 480 + pub async fn delete_user(&self, username: &str) -> Result<(), anyhow::Error> { 481 + let db = &mut self.diesel.get().await?; 482 + 483 + let changes = 484 + diesel::delete(creds::table.filter(creds::username.eq(username))).execute(db).await?; 485 + if changes != 1 { 486 + return Err(anyhow::anyhow!("expected a single row deletion, got {changes}")); 487 + } 488 + Ok(()) 489 + } 490 + 491 + pub async fn set_password( 492 + &self, username: &str, password: String, iters: u32, 493 + ) -> Result<(), anyhow::Error> { 494 + let (salt, hash) = 495 + tokio::task::spawn_blocking(move || Creds::generate_creds(&password, iters)).await?; 496 + 497 + let db = &mut self.diesel.get().await?; 498 + let rows = diesel::update(creds::table) 499 + .filter(creds::username.eq(username)) 500 + .set(( 501 + creds::passwd_iters.eq(iters as i32), 502 + creds::passwd_salt.eq(&salt[..]), 503 + creds::passwd_hash.eq(&hash[..]), 504 + )) 505 + .execute(db) 506 + .await?; 507 + if rows != 1 { 508 + return Err(anyhow::anyhow!("updated {rows} rows, expected to update 1")); 509 + } 510 + Ok(()) 511 + } 512 + 513 + pub async fn add_user( 514 + &self, username: &str, email: &str, is_admin: bool, 515 + ) -> Result<u32, anyhow::Error> { 516 + let db = &mut self.diesel.get().await?; 517 + 518 + diesel::insert_into(creds::table) 519 + .values(&Creds { 520 + username: username.into(), 521 + email: email.into(), 522 + passwd_salt: None, 523 + passwd_iters: 10_000, 524 + passwd_hash: None, 525 + last_active: None, 526 + is_enabled: true, 527 + is_admin, 528 + }) 529 + .returning(creds::id) 530 + .get_result::<i32>(db) 531 + .await 532 + .map_err(|v| v.into()) 533 + .map(|v| v as u32) 534 + } 535 + 536 + pub async fn get_user_by_username( 537 + &self, username: &str, 538 + ) -> Result<(i32, Creds), anyhow::Error> { 539 + let db = &mut self.diesel.get().await?; 540 + 541 + Ok(creds::table 542 + .select((creds::id, Creds::as_select())) 543 + .filter(creds::username.eq(username)) 544 + .get_result::<(i32, Creds)>(db) 545 + .await?) 546 + } 547 + 548 + pub async fn get_user_by_id(&self, id: i32) -> Result<Creds, anyhow::Error> { 549 + let db = &mut self.diesel.get().await?; 550 + 551 + Ok(creds::table.select(Creds::as_select()).filter(creds::id.eq(id)).get_result(db).await?) 552 + } 553 + 554 + pub async fn update_last_active(&self, user_id: i32) -> Result<(), anyhow::Error> { 555 + let mut db = self.diesel.get().await?; 556 + diesel::update(schema::creds::table) 557 + .filter(schema::creds::id.eq(user_id)) 558 + .set((schema::creds::last_active.eq(time::OffsetDateTime::now_utc()),)) 559 + .execute(&mut db) 560 + .await?; 561 + Ok(()) 562 + } 563 + } 564 + 565 + // This is eww, but it's the fastest. 566 + struct BestMds<'a>(&'a [&'a [u8]]); 567 + impl<'a> QueryFragment<diesel::pg::Pg> for BestMds<'a> { 568 + fn walk_ast<'b>( 569 + &'b self, mut pass: diesel::query_builder::AstPass<'_, 'b, diesel::pg::Pg>, 570 + ) -> diesel::QueryResult<()> { 571 + pass.push_sql( 572 + r#"WITH best AS ( 573 + select chksum,MAX(rank) as maxrank from funcs f1 574 + WHERE chksum = ANY("#, 575 + ); 576 + pass.push_bind_param::<Array<Binary>, _>(&self.0)?; 577 + pass.push_sql( 578 + r#") 579 + GROUP BY chksum 580 + ) 581 + SELECT f2.name,f2.len,f2.metadata,f2.chksum FROM best 582 + LEFT JOIN funcs f2 ON (best.chksum=f2.chksum AND best.maxrank=f2.rank)"#, 583 + ); 584 + Ok(()) 585 + } 586 + } 587 + impl<'a> diesel::query_builder::QueryId for BestMds<'a> { 588 + type QueryId = BestMds<'static>; 589 + } 590 + impl<'a> Query for BestMds<'a> { 591 + type SqlType = (VarChar, Integer, Binary, Binary); 592 + }
+95
common/src/db/schema.rs
···
··· 1 + use std::borrow::Cow; 2 + 3 + use diesel::{Insertable, Queryable, Selectable}; 4 + use log::error; 5 + use pbkdf2::{hmac::Hmac, pbkdf2}; 6 + use sha2::Sha256; 7 + 8 + pub use super::schema_auto::*; 9 + 10 + diesel::table! { 11 + func_ranks { 12 + id -> Int4, 13 + name -> Text, 14 + len -> Int4, 15 + db_id -> Int4, 16 + chksum -> Nullable<Bytea>, 17 + metadata -> Nullable<Bytea>, 18 + rank -> Nullable<Int4>, 19 + push_dt -> Nullable<Timestamptz>, 20 + update_dt -> Nullable<Timestamptz>, 21 + } 22 + } 23 + 24 + diesel::joinable!(func_ranks -> dbs (id)); 25 + 26 + #[derive(Insertable, Queryable, Selectable, Default)] 27 + #[diesel(table_name = creds)] 28 + pub struct Creds<'a> { 29 + pub username: Cow<'a, str>, 30 + pub email: Cow<'a, str>, 31 + 32 + pub passwd_salt: Option<Cow<'a, [u8]>>, 33 + pub passwd_iters: i32, 34 + pub passwd_hash: Option<Cow<'a, [u8]>>, 35 + 36 + pub last_active: Option<time::OffsetDateTime>, 37 + 38 + pub is_admin: bool, 39 + pub is_enabled: bool, 40 + } 41 + 42 + impl<'a> Creds<'a> { 43 + pub fn verify_password(&self, password: &str) -> bool { 44 + let salt = if let Some(v) = self.passwd_salt.as_ref() { 45 + v 46 + } else { 47 + return false; 48 + }; 49 + let current_hash = if let Some(v) = self.passwd_hash.as_ref() { 50 + v 51 + } else { 52 + return false; 53 + }; 54 + if self.passwd_iters <= 0 { 55 + return false; 56 + } 57 + 58 + let mut hash = vec![0u8; 32]; 59 + if pbkdf2::<Hmac<Sha256>>(password.as_bytes(), salt, self.passwd_iters as u32, &mut hash) 60 + .is_err() 61 + { 62 + error!("invalid output digest length"); 63 + return false; 64 + } 65 + 66 + hash == current_hash.as_ref() 67 + } 68 + 69 + pub(crate) fn generate_creds(password: &str, iters: u32) -> ([u8; 4], [u8; 32]) { 70 + let salt: [u8; 4] = rand::random(); 71 + let mut res = [0u8; 32]; 72 + pbkdf2::pbkdf2::<Hmac<Sha256>>(password.as_bytes(), &salt, iters, &mut res) 73 + .expect("failed to perform pbkdf2_hmac_sha256"); 74 + (salt, res) 75 + } 76 + } 77 + 78 + #[cfg(test)] 79 + mod tests { 80 + use super::*; 81 + 82 + #[test] 83 + fn generate_and_verify_password() { 84 + let password = "MyPassword1$"; 85 + let iters = 10_000; 86 + let (salt, hash) = Creds::generate_creds(password, iters); 87 + let creds = Creds { 88 + passwd_hash: Some((&hash[..]).into()), 89 + passwd_salt: Some((&salt[..]).into()), 90 + passwd_iters: iters as i32, 91 + ..Default::default() 92 + }; 93 + assert!(creds.verify_password(password), "failed to verify password"); 94 + } 95 + }
+76
common/src/db/schema_auto.rs
···
··· 1 + // @generated automatically by Diesel CLI. 2 + 3 + diesel::table! { 4 + creds (id) { 5 + id -> Int4, 6 + #[max_length = 256] 7 + username -> Varchar, 8 + #[max_length = 256] 9 + email -> Varchar, 10 + passwd_salt -> Nullable<Bytea>, 11 + passwd_iters -> Int4, 12 + passwd_hash -> Nullable<Bytea>, 13 + last_active -> Nullable<Timestamptz>, 14 + creation_dt -> Timestamptz, 15 + is_admin -> Bool, 16 + is_enabled -> Bool, 17 + } 18 + } 19 + 20 + diesel::table! { 21 + dbs (id) { 22 + id -> Int4, 23 + #[max_length = 260] 24 + file_path -> Nullable<Varchar>, 25 + #[max_length = 260] 26 + idb_path -> Nullable<Varchar>, 27 + file_id -> Nullable<Int4>, 28 + user_id -> Nullable<Int4>, 29 + } 30 + } 31 + 32 + diesel::table! { 33 + files (id) { 34 + id -> Int4, 35 + chksum -> Nullable<Bytea>, 36 + } 37 + } 38 + 39 + diesel::table! { 40 + funcs (id) { 41 + id -> Int4, 42 + name -> Text, 43 + len -> Int4, 44 + db_id -> Int4, 45 + chksum -> Nullable<Bytea>, 46 + metadata -> Nullable<Bytea>, 47 + rank -> Nullable<Int4>, 48 + push_dt -> Nullable<Timestamptz>, 49 + update_dt -> Nullable<Timestamptz>, 50 + } 51 + } 52 + 53 + diesel::table! { 54 + users (id) { 55 + id -> Int4, 56 + lic_id -> Nullable<Bytea>, 57 + lic_data -> Nullable<Bytea>, 58 + #[max_length = 260] 59 + hostname -> Nullable<Varchar>, 60 + first_seen -> Nullable<Timestamptz>, 61 + cred_id -> Nullable<Int4>, 62 + } 63 + } 64 + 65 + diesel::joinable!(dbs -> files (file_id)); 66 + diesel::joinable!(dbs -> users (user_id)); 67 + diesel::joinable!(funcs -> dbs (db_id)); 68 + diesel::joinable!(users -> creds (cred_id)); 69 + 70 + diesel::allow_tables_to_appear_in_same_query!( 71 + creds, 72 + dbs, 73 + files, 74 + funcs, 75 + users, 76 + );
+10 -3
common/src/lib.rs
··· 3 4 use std::fmt::Write; 5 6 - pub mod db; 7 pub mod config; 8 pub mod md; 9 pub mod rpc; 10 pub mod web; 11 ··· 13 pub db: db::Database, 14 pub config: std::sync::Arc<config::Config>, 15 pub server_name: String, 16 } 17 18 pub type SharedState = std::sync::Arc<SharedState_>; ··· 22 const CHUNK_SIZE: usize = 32; 23 data.chunks(CHUNK_SIZE).for_each(|chunk| { 24 for &ch in chunk { 25 - let _ = write!(&mut output, "{:02x} ", ch); 26 } 27 let padding = CHUNK_SIZE - chunk.len(); 28 for _ in 0..padding { ··· 30 } 31 32 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('.')) { 34 if !ch.is_ascii_graphic() { 35 output.push('.'); 36 } else {
··· 3 4 use std::fmt::Write; 5 6 + pub mod async_drop; 7 pub mod config; 8 + pub mod db; 9 pub mod md; 10 + pub mod metrics; 11 pub mod rpc; 12 pub mod web; 13 ··· 15 pub db: db::Database, 16 pub config: std::sync::Arc<config::Config>, 17 pub server_name: String, 18 + pub metrics: metrics::Metrics, 19 } 20 21 pub type SharedState = std::sync::Arc<SharedState_>; ··· 25 const CHUNK_SIZE: usize = 32; 26 data.chunks(CHUNK_SIZE).for_each(|chunk| { 27 for &ch in chunk { 28 + let _ = write!(&mut output, "{ch:02x} "); 29 } 30 let padding = CHUNK_SIZE - chunk.len(); 31 for _ in 0..padding { ··· 33 } 34 35 let _ = write!(&mut output, " | "); 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 + { 41 if !ch.is_ascii_graphic() { 42 output.push('.'); 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 + }
+153 -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 result: u32, 7 + pub error: &'a str, 8 + } 9 + 10 + #[derive(Serialize, Deserialize)] 11 + pub struct RpcNotify<'a> { 12 + pub ty: u32, 13 + pub text: &'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 client_version: u32, 25 + pub license_data: &'a [u8], 26 + pub lic_number: [u8; 6], 27 + pub record_conv: u32, 28 + } 29 + 30 + #[derive(Debug, Deserialize, Serialize, Clone)] 31 + pub struct PatternId<'a> { 32 + pub ty: u32, 33 + pub data: &'a [u8], 34 + } 35 + 36 + #[derive(Deserialize, Serialize)] 37 + pub struct PullMetadata<'a> { 38 + pub flags: u32, 39 + pub keys: Cow<'a, [u32]>, 40 + 41 + #[serde(borrow)] 42 + pub pattern_ids: Cow<'a, [PatternId<'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 codes: 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 + pub pattern_id: PatternId<'a>, 66 + } 67 + 68 + #[derive(Deserialize, Serialize)] 69 + pub struct PushMetadata<'a> { 70 + pub flags: u32, 71 + pub idb_path: &'a str, 72 + pub input_path: &'a str, 73 + pub input_md5: [u8; 16], 74 + pub hostname: &'a str, 75 + pub funcs: Cow<'a, [PushMetadataFunc<'a>]>, 76 + pub ea64s: Cow<'a, [u64]>, 77 + } 78 + 79 + #[derive(Deserialize, Serialize)] 80 + pub struct PushMetadataResult<'a> { 81 + // array of 0=exists, 1=NEW 82 + pub status: Cow<'a, [u32]>, 83 + } 84 + 85 + #[derive(Debug, Deserialize, Serialize)] 86 + pub struct DelHistory<'a> { 87 + pub flags: u32, // =0x08 88 + pub license_ids: Cow<'a, [Cow<'a, str>]>, 89 + pub time_ranges: Cow<'a, [[u64; 2]]>, 90 + pub history_id_ranges: Cow<'a, [[u64; 2]]>, 91 + pub idbs: Cow<'a, [Cow<'a, str>]>, 92 + pub inputs: Cow<'a, [Cow<'a, str>]>, 93 + pub funcs: Cow<'a, [Cow<'a, str>]>, // funcs 94 + pub usernames: Cow<'a, [Cow<'a, str>]>, 95 + pub input_hashes: Cow<'a, [Cow<'a, [u8; 16]>]>, 96 + pub calcrel_hashes: Cow<'a, [Cow<'a, [u8; 16]>]>, 97 + pub push_id_ranges: Cow<'a, [[u64; 2]]>, 98 + pub max_entries: u64, 99 + } 100 + 101 + #[derive(Deserialize, Serialize)] 102 + pub struct DelHistoryResult { 103 + pub ndeleted: u32, 104 + } 105 + 106 + #[derive(Debug, Deserialize, Serialize, Default)] 107 + pub struct LicenseInfo<'a> { 108 + pub id: Cow<'a, str>, 109 + pub name: Cow<'a, str>, 110 + pub email: Cow<'a, str>, 111 + } 112 + 113 + #[derive(Debug, Deserialize, Serialize, Default)] 114 + pub struct HelloResult<'a> { 115 + pub license_info: LicenseInfo<'a>, 116 + pub username: Cow<'a, str>, 117 + pub karma: u32, 118 + pub last_active: u64, 119 + pub features: u32, 120 + } 121 + 122 + #[derive(Debug, Deserialize, Serialize)] 123 + pub struct GetFuncHistories<'a> { 124 + #[serde(borrow)] 125 + pub funcs: Cow<'a, [PatternId<'a>]>, 126 + pub flags: u32, 127 + } 128 + 129 + #[derive(Debug, Deserialize, Serialize, Clone)] 130 + pub struct FunctionHistory<'a> { 131 + pub unk0: u64, 132 + pub unk1: u64, 133 + pub name: Cow<'a, str>, 134 + pub metadata: Cow<'a, [u8]>, 135 + pub timestamp: u64, 136 + pub author_idx: u32, 137 + pub idb_path_idx: u32, 138 + } 139 + 140 + #[derive(Debug, Deserialize, Serialize, Clone)] 141 + pub struct FunctionHistories<'a> { 142 + #[serde(borrow)] 143 + pub log: Cow<'a, [FunctionHistory<'a>]>, 144 + } 145 + 146 + #[derive(Debug, Deserialize, Serialize)] 147 + pub struct GetFuncHistoriesResult<'a> { 148 + pub status: Cow<'a, [u32]>, 149 + #[serde(borrow)] 150 + pub funcs: Cow<'a, [FunctionHistories<'a>]>, 151 + pub authors: Cow<'a, [Cow<'a, str>]>, 152 + pub idb_paths: Cow<'a, [Cow<'a, str>]>, 153 + }
+281 -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 struct PacketHeader { 67 + code: u8, 68 + size: usize, 69 + } 70 + 71 + pub async fn read_packet_header<R: AsyncRead + Unpin>( 72 + mut reader: R, 73 + ) -> Result<PacketHeader, Error> { 74 + let mut head = [0u8; 5]; 75 + match reader.read_exact(&mut head).await { 76 + Ok(_) => {}, 77 + Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Err(Error::Eof), // client decided to disconnect... 78 + Err(e) => return Err(e.into()), 79 + } 80 + let code = head[4]; 81 + let mut buf_len = [0u8; 4]; 82 + buf_len.copy_from_slice(&head[..4]); 83 + 84 + let buf_len = u32::from_be_bytes(buf_len) as usize; 85 + if buf_len < 4 { 86 + return Err(std::io::Error::new( 87 + std::io::ErrorKind::InvalidData, 88 + "payload size is too small", 89 + ) 90 + .into()); 91 + } 92 + 93 + Ok(PacketHeader { code, size: buf_len }) 94 + } 95 + 96 + impl PacketHeader { 97 + pub async fn read<R: AsyncRead + Unpin>(self, mut reader: R) -> Result<Vec<u8>, Error> { 98 + trace!("expecting {} bytes...", self.size); 99 + let buf_len = self.size + 1; 100 + 101 + let max_len = get_code_maxlen(self.code); 102 + if self.size > max_len { 103 + info!("maxium size exceeded: code={}: max={}; req={}", self.code, max_len, self.size); 104 + return Err(std::io::Error::new( 105 + std::io::ErrorKind::InvalidData, 106 + "request length exceeded maximum limit", 107 + ) 108 + .into()); 109 + } 110 + 111 + let mut data = Vec::new(); 112 + data.try_reserve_exact(buf_len)?; 113 + data.resize(buf_len, 0); 114 + data[0] = self.code; 115 + reader.read_exact(&mut data[1..]).await?; 116 + 117 + Ok(data) 118 + } 119 + 120 + // returns true if this could be an http request. 121 + pub fn is_http(&self) -> bool { 122 + let mut sz = (self.size as u32).to_be_bytes(); 123 + sz.make_ascii_uppercase(); 124 + let sz = &sz[..]; 125 + 126 + match sz { 127 + b"GET " | b"PUT " if self.code == b'/' => true, 128 + b"POST" | b"HEAD" if self.code == b' ' => true, 129 + _ => false, 130 + } 131 + } 132 + } 133 + 134 + async fn write_packet<W: AsyncWrite + Unpin>(mut w: W, data: &[u8]) -> Result<(), std::io::Error> { 135 + let buf_len: u32 = (data.len() - 1) as u32; 136 + let buf_len = buf_len.to_be_bytes(); 137 + w.write_all(&buf_len).await?; 138 + w.write_all(data).await?; 139 + Ok(()) 140 + } 141 + 142 + pub enum RpcMessage<'a> { 143 + Ok(()), 144 + Fail(RpcFail<'a>), 145 + Notify(RpcNotify<'a>), 146 + Hello(RpcHello<'a>, Option<Creds<'a>>), 147 + PullMetadata(PullMetadata<'a>), 148 + PullMetadataResult(PullMetadataResult<'a>), 149 + PushMetadata(PushMetadata<'a>), 150 + PushMetadataResult(PushMetadataResult<'a>), 151 + DelHistory(DelHistory<'a>), 152 + DelHistoryResult(DelHistoryResult), 153 + GetFuncHistories(GetFuncHistories<'a>), 154 + GetFuncHistoriesResult(GetFuncHistoriesResult<'a>), 155 + HelloResult(HelloResult<'a>), 156 + } 157 + 158 + impl<'a> serde::Serialize for RpcMessage<'a> { 159 + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 160 + use serde::ser::SerializeTuple; 161 + 162 + let code = self.get_code(); 163 + let mut tuple = serializer.serialize_tuple(2)?; 164 + 165 + // u8 is pushed without further encoding... 166 + tuple.serialize_element(&code)?; 167 + 168 + match self { 169 + RpcMessage::Ok(msg) => tuple.serialize_element(msg)?, 170 + RpcMessage::Fail(msg) => tuple.serialize_element(msg)?, 171 + RpcMessage::Notify(msg) => tuple.serialize_element(msg)?, 172 + RpcMessage::Hello(msg, _) => tuple.serialize_element(msg)?, 173 + RpcMessage::PullMetadata(msg) => tuple.serialize_element(msg)?, 174 + RpcMessage::PullMetadataResult(msg) => tuple.serialize_element(msg)?, 175 + RpcMessage::PushMetadata(msg) => tuple.serialize_element(msg)?, 176 + RpcMessage::PushMetadataResult(msg) => tuple.serialize_element(msg)?, 177 + RpcMessage::DelHistory(msg) => tuple.serialize_element(msg)?, 178 + RpcMessage::DelHistoryResult(msg) => tuple.serialize_element(msg)?, 179 + RpcMessage::GetFuncHistories(msg) => tuple.serialize_element(msg)?, 180 + RpcMessage::GetFuncHistoriesResult(msg) => tuple.serialize_element(msg)?, 181 + RpcMessage::HelloResult(msg) => tuple.serialize_element(msg)?, 182 + } 183 + 184 + tuple.end() 185 + } 186 + } 187 + 188 + impl<'a> RpcMessage<'a> { 189 + fn deserialize_check<T: serde::Deserialize<'a>>(payload: &'a [u8]) -> Result<T, Error> { 190 + let v = de::from_slice(payload)?; 191 + if v.1 != payload.len() { 192 + let bytes_remaining = crate::make_pretty_hex(&payload[v.1..]); 193 + trace!( 194 + "{} remaining bytes after deserializing {}\n{bytes_remaining}", 195 + payload.len() - v.1, 196 + std::any::type_name::<T>() 197 + ); 198 + } 199 + Ok(v.0) 200 + } 201 + 202 + pub fn deserialize(payload: &'a [u8]) -> Result<RpcMessage<'a>, Error> { 203 + let msg_type = payload[0]; 204 + let payload = &payload[1..]; 205 + 206 + let res = match msg_type { 207 + 0x0a => { 208 + if !payload.is_empty() { 209 + trace!( 210 + "Ok message with additional data: {} bytes: {payload:02x?}", 211 + payload.len() 212 + ); 213 + } 214 + RpcMessage::Ok(()) 215 + }, 216 + 0x0b => RpcMessage::Fail(Self::deserialize_check(payload)?), 217 + 0x0c => RpcMessage::Notify(Self::deserialize_check(payload)?), 218 + 0x0d => { 219 + let (hello, consumed) = de::from_slice::<messages::RpcHello>(payload)?; 220 + let creds = if payload.len() > consumed && hello.client_version > 2 { 221 + let payload = &payload[consumed..]; 222 + let (creds, consumed) = de::from_slice::<Creds>(payload)?; 223 + if payload.len() != consumed { 224 + trace!("bytes remaining after HelloV2: {payload:02x?}"); 225 + } 226 + Some(creds) 227 + } else { 228 + if hello.client_version > 2 || payload.len() != consumed { 229 + trace!("Unexpected Hello msg: {payload:02x?}"); 230 + } 231 + None 232 + }; 233 + RpcMessage::Hello(hello, creds) 234 + }, 235 + 0x0e => RpcMessage::PullMetadata(Self::deserialize_check(payload)?), 236 + 0x0f => RpcMessage::PullMetadataResult(Self::deserialize_check(payload)?), 237 + 0x10 => RpcMessage::PushMetadata(Self::deserialize_check(payload)?), 238 + 0x11 => RpcMessage::PushMetadataResult(Self::deserialize_check(payload)?), 239 + 0x18 => RpcMessage::DelHistory(Self::deserialize_check(payload)?), 240 + 0x19 => RpcMessage::DelHistoryResult(Self::deserialize_check(payload)?), 241 + 0x2f => RpcMessage::GetFuncHistories(Self::deserialize_check(payload)?), 242 + 0x30 => RpcMessage::GetFuncHistoriesResult(Self::deserialize_check(payload)?), 243 + 0x31 => RpcMessage::HelloResult(Self::deserialize_check(payload)?), 244 + _ => { 245 + trace!("got invalid message type '{:02x}'", msg_type); 246 + return Err(Error::InvalidData); 247 + }, 248 + }; 249 + 250 + Ok(res) 251 + } 252 + 253 + pub async fn async_write<W: AsyncWrite + Unpin>(&self, w: W) -> Result<(), Error> { 254 + let mut output = Vec::with_capacity(32); 255 + ser::to_writer(self, &mut output)?; 256 + 257 + write_packet(w, &output).await?; 258 + 259 + Ok(()) 260 + } 261 + 262 + fn get_code(&self) -> u8 { 263 + use RpcMessage::*; 264 + 265 + match self { 266 + Ok(_) => 0x0a, 267 + Fail(_) => 0x0b, 268 + Notify(_) => 0x0c, 269 + Hello(..) => 0x0d, 270 + PullMetadata(_) => 0x0e, 271 + PullMetadataResult(_) => 0x0f, 272 + PushMetadata(_) => 0x10, 273 + PushMetadataResult(_) => 0x11, 274 + DelHistory(_) => 0x18, 275 + DelHistoryResult(_) => 0x19, 276 + GetFuncHistories(_) => 0x2f, 277 + GetFuncHistoriesResult(_) => 0x30, 278 + HelloResult(_) => 0x31, 279 + } 280 + } 281 + }
+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::PatternId { ty: 1, data: &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 + }
+13 -2
config-example.toml
··· 6 # server display name; appears in IDA output window 7 server_name = "lumen" 8 9 # only required when `use_tls` is set to true. 10 [lumina.tls] 11 - # Specify the server's certificate. 12 # Clients connecting to the server must match this certificate. 13 # If the certificate is password protected, the password can be specified in the `PKCSPASSWD` environment variable. 14 server_cert = "path/to/server_crt" 15 16 [database] 17 # 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" 19 # Sets if the database connection should be made using TLS. 20 use_tls = false 21 # If the database requires a secure connection, paths to server-ca and client-id certificates can be set here:
··· 6 # server display name; appears in IDA output window 7 server_name = "lumen" 8 9 + # Allow clients to delete metadata from the database? 10 + allow_deletes = true 11 + # How many function histories should we return? 0=Disabled. 12 + get_history_limit = 50 13 + 14 + [users] 15 + # Enable guest accounts? disabling this will only allow IDA 8.1+ to connect. 16 + allow_guests = true 17 + # sets the amount of PBKDF2 iterations for storing passwords. 18 + pbkdf2_iterations = 120000 19 + 20 # only required when `use_tls` is set to true. 21 [lumina.tls] 22 + # Specify the server's certificate. 23 # Clients connecting to the server must match this certificate. 24 # If the certificate is password protected, the password can be specified in the `PKCSPASSWD` environment variable. 25 server_cert = "path/to/server_crt" 26 27 [database] 28 # 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 29 + connection_info = "postgres://postgres:1@127.0.0.1/postgres" 30 # Sets if the database connection should be made using TLS. 31 use_tls = false 32 # 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' 2 3 - volumes: 4 - postgres_data: 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
··· 1 + name: lumen 2 + volumes: 3 + postgres_data: 4 5 services: 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 echo "Exiting due to error: $@" && exit 1 6 } 7 do_config_fixup(){ 8 - sed -i -e "s,connection_info.*,connection_info = \"host=db port=5432 user=lumina password=1\"," \ 9 /lumen/config.toml 10 } 11 use_default_config(){ ··· 66 67 setup_config ; 68 do_config_fixup ; 69 - lumen -c /lumen/config.toml || die "Launching lumen";
··· 5 echo "Exiting due to error: $@" && exit 1 6 } 7 do_config_fixup(){ 8 + sed -i -e "s,connection_info.*,connection_info = \"${DATABASE_URL}\"," \ 9 /lumen/config.toml 10 } 11 use_default_config(){ ··· 66 67 setup_config ; 68 do_config_fixup ; 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
+9 -7
lumen/Cargo.toml
··· 3 description = "lumen server" 4 version = "0.2.0" 5 authors = ["Naim A. <naim@abda.nl>"] 6 - edition = "2018" 7 publish = false 8 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" 15 tokio-native-tls = "0.3" 16 - native-tls = {version = "0.2"} 17 warp = "0.3"
··· 3 description = "lumen server" 4 version = "0.2.0" 5 authors = ["Naim A. <naim@abda.nl>"] 6 + edition = "2021" 7 publish = false 8 9 [dependencies] 10 + common = { path = "../common" } 11 + tokio = { version = "1.32", features = ["full"] } 12 + log = { version = "0.4", features = ["release_max_level_debug"] } 13 + pretty_env_logger = "0.5" 14 + clap = "4.3" 15 tokio-native-tls = "0.3" 16 + native-tls = { version = "0.2" } 17 warp = "0.3" 18 + prometheus-client = "0.22.0" 19 + rpassword = "7.3.1"
+68 -356
lumen/src/main.rs
··· 4 #![warn(unused_crate_dependencies)] 5 #![deny(clippy::all)] 6 7 - use common::rpc::{RpcHello, RpcFail}; 8 - use native_tls::Identity; 9 - use clap::Arg; 10 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_}; 19 20 mod web; 21 - 22 - use common::{config, make_pretty_hex, md, rpc::{self, Error}}; 23 - use common::db::Database; 24 - use rpc::RpcMessage; 25 26 fn setup_logger() { 27 if std::env::var("RUST_LOG").is_err() { ··· 30 pretty_env_logger::init_timed(); 31 } 32 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() { 266 - setup_logger(); 267 let matches = clap::Command::new("lumen") 268 .version(env!("CARGO_PKG_VERSION")) 269 .about("lumen is a private Lumina server for IDA.\nVisit https://github.com/naim94a/lumen/ for updates.") ··· 271 .arg( 272 Arg::new("config") 273 .short('c') 274 - .required(true) 275 .default_value("config.toml") 276 .help("Configuration file path") 277 ) 278 .get_matches(); 279 280 let config = { 281 - config::load_config(std::fs::File::open(matches.get_one::<String>("config").unwrap()).expect("failed to read config")) 282 }; 283 let config = Arc::new(config); 284 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); 392 }
··· 4 #![warn(unused_crate_dependencies)] 5 #![deny(clippy::all)] 6 7 + use clap::{builder::BoolishValueParser, Arg, Command}; 8 use log::*; 9 + use server::do_lumen; 10 + use std::sync::Arc; 11 + use users::UserMgmt; 12 13 + mod server; 14 + mod users; 15 mod web; 16 17 fn setup_logger() { 18 if std::env::var("RUST_LOG").is_err() { ··· 21 pretty_env_logger::init_timed(); 22 } 23 24 + #[tokio::main] 25 + async fn main() { 26 let matches = clap::Command::new("lumen") 27 .version(env!("CARGO_PKG_VERSION")) 28 .about("lumen is a private Lumina server for IDA.\nVisit https://github.com/naim94a/lumen/ for updates.") ··· 30 .arg( 31 Arg::new("config") 32 .short('c') 33 .default_value("config.toml") 34 .help("Configuration file path") 35 ) 36 + .subcommand( 37 + Command::new("users") 38 + .about("User Management") 39 + .subcommand( 40 + Command::new("add") 41 + .about("Adds a user") 42 + .arg( 43 + Arg::new("username") 44 + .required(true) 45 + ) 46 + .arg( 47 + Arg::new("email") 48 + .required(true) 49 + ) 50 + .arg( 51 + Arg::new("is_admin") 52 + .required(false) 53 + .default_value("no") 54 + .value_parser(BoolishValueParser::new()) 55 + ) 56 + ) 57 + .subcommand( 58 + Command::new("del") 59 + .about("Deletes a user") 60 + .arg(Arg::new("username")) 61 + ) 62 + ) 63 + .subcommand(Command::new("passwd").about("Set user password").arg(Arg::new("username").required(true))) 64 .get_matches(); 65 66 let config = { 67 + common::config::load_config( 68 + std::fs::File::open(matches.get_one::<String>("config").unwrap()) 69 + .expect("failed to read config"), 70 + ) 71 }; 72 let config = Arc::new(config); 73 74 + match matches.subcommand() { 75 + Some(("users", m)) => { 76 + let users = UserMgmt::new(&config).await; 77 + match m.subcommand() { 78 + None => users.list_users().await, 79 + Some(("add", m)) => { 80 + let username = m.get_one::<String>("username").unwrap(); 81 + let email = m.get_one::<String>("email").unwrap(); 82 + let is_admin = *m.get_one::<bool>("is_admin").unwrap_or(&false); 83 + users.add_user(username, email, is_admin).await; 84 + }, 85 + Some(("del", m)) => { 86 + let username = m.get_one::<String>("username").unwrap(); 87 + users.delete_user(username).await; 88 + }, 89 + _ => unreachable!(), 90 + }; 91 }, 92 + Some(("passwd", m)) => { 93 + let username = m.get_one::<String>("username").unwrap(); 94 + let password = rpassword::prompt_password("New Password: ").unwrap(); 95 + let users = UserMgmt::new(&config).await; 96 + users.set_password(username, &password).await; 97 + }, 98 + Some(_) => unreachable!(), 99 + None => { 100 + setup_logger(); 101 + do_lumen(config).await 102 + }, 103 }; 104 }
+651
lumen/src/server.rs
···
··· 1 + use std::{ 2 + borrow::Cow, 3 + collections::HashMap, 4 + mem::discriminant, 5 + process::exit, 6 + sync::Arc, 7 + time::{Duration, Instant}, 8 + }; 9 + 10 + use common::{ 11 + async_drop::AsyncDropper, 12 + config::Config, 13 + db::{self, Database}, 14 + make_pretty_hex, md, 15 + metrics::LuminaVersion, 16 + rpc::{self, Creds, Error, HelloResult, RpcFail, RpcHello, RpcMessage}, 17 + SharedState, SharedState_, 18 + }; 19 + use log::{debug, error, info, trace, warn}; 20 + use native_tls::Identity; 21 + use tokio::{ 22 + io::{AsyncRead, AsyncWrite}, 23 + net::TcpListener, 24 + time::timeout, 25 + }; 26 + 27 + use crate::web; 28 + 29 + struct Session<'a> { 30 + state: &'a SharedState_, 31 + hello_msg: RpcHello<'a>, 32 + _creds: db::schema::Creds<'a>, 33 + creds_id: Option<i32>, 34 + last_cred_check: Instant, 35 + } 36 + impl<'a> Session<'a> { 37 + /// Check if the user changed in the database. 38 + pub async fn is_valid(&mut self) -> bool { 39 + let db = &self.state.db; 40 + if let Some(cred_id) = self.creds_id { 41 + if self.last_cred_check.elapsed() > Duration::from_secs(60 * 5) { 42 + match db.get_user_by_id(cred_id).await { 43 + Ok(v) => { 44 + if !v.is_enabled 45 + || v.is_admin != self._creds.is_admin 46 + || v.passwd_salt != self._creds.passwd_salt 47 + { 48 + // user changed, force them to login again. 49 + return false; 50 + } 51 + self.last_cred_check = Instant::now(); 52 + return true; 53 + }, 54 + Err(err) => { 55 + error!("db error: {err}"); 56 + return false; 57 + }, 58 + } 59 + } 60 + } 61 + true 62 + } 63 + } 64 + 65 + async fn handle_transaction<'a, S: AsyncRead + AsyncWrite + Unpin>( 66 + session: &mut Session<'a>, mut stream: S, 67 + ) -> Result<(), Error> { 68 + let state = session.state; 69 + let db = &state.db; 70 + let server_name = state.server_name.as_str(); 71 + 72 + trace!("waiting for command.."); 73 + let rpkt = async { 74 + let hdr = rpc::read_packet_header(&mut stream).await?; 75 + // we don't want to read a whole request just to find out the user was revoked... 76 + if !session.is_valid().await { 77 + return Err(Error::Timeout); 78 + } 79 + hdr.read(&mut stream).await 80 + }; 81 + let req = match timeout(Duration::from_secs(3600), rpkt).await { 82 + Ok(res) => match res { 83 + Ok(v) => v, 84 + Err(e) => return Err(e), 85 + }, 86 + Err(_) => { 87 + _ = RpcMessage::Fail(RpcFail { 88 + result: 0, 89 + error: &format!("{server_name} client idle for too long.\n"), 90 + }) 91 + .async_write(&mut stream) 92 + .await; 93 + return Err(Error::Timeout); 94 + }, 95 + }; 96 + trace!("got command!"); 97 + 98 + if !session.is_valid().await { 99 + return Err(Error::Timeout); 100 + } 101 + 102 + let req = match RpcMessage::deserialize(&req) { 103 + Ok(v) => v, 104 + Err(err) => { 105 + warn!("bad message: \n{}\n", make_pretty_hex(&req)); 106 + error!("failed to process rpc message: {}", err); 107 + let resp = rpc::RpcFail { 108 + result: 0, 109 + error: &format!("{server_name}: error: invalid data.\n"), 110 + }; 111 + let resp = RpcMessage::Fail(resp); 112 + resp.async_write(&mut stream).await?; 113 + 114 + return Ok(()); 115 + }, 116 + }; 117 + match req { 118 + RpcMessage::PullMetadata(md) => { 119 + let start = Instant::now(); 120 + let funcs = match timeout(Duration::from_secs(4 * 60), db.get_funcs(&md.pattern_ids)) 121 + .await 122 + { 123 + Ok(r) => match r { 124 + Ok(v) => v, 125 + Err(e) => { 126 + error!("pull failed, db: {}", e); 127 + rpc::RpcMessage::Fail(rpc::RpcFail { 128 + result: 0, 129 + error: &format!("{server_name}: db error; please try again later..\n"), 130 + }) 131 + .async_write(&mut stream) 132 + .await?; 133 + return Ok(()); 134 + }, 135 + }, 136 + Err(_) => { 137 + RpcMessage::Fail(RpcFail { 138 + result: 0, 139 + error: &format!("{server_name}: query took too long to execute.\n"), 140 + }) 141 + .async_write(&mut stream) 142 + .await?; 143 + debug!("pull query timeout"); 144 + return Err(Error::Timeout); 145 + }, 146 + }; 147 + let pulled_funcs = funcs.iter().filter(|v| v.is_some()).count(); 148 + state.metrics.pulls.inc_by(pulled_funcs as _); 149 + state.metrics.queried_funcs.inc_by(md.pattern_ids.len() as _); 150 + debug!( 151 + "pull {pulled_funcs}/{} funcs ended after {:?}", 152 + md.pattern_ids.len(), 153 + start.elapsed() 154 + ); 155 + 156 + let statuses: Vec<u32> = funcs.iter().map(|v| u32::from(v.is_none())).collect(); 157 + let found = funcs 158 + .into_iter() 159 + .flatten() 160 + .map(|v| rpc::PullMetadataResultFunc { 161 + popularity: v.popularity, 162 + len: v.len, 163 + name: Cow::Owned(v.name), 164 + mb_data: Cow::Owned(v.data), 165 + }) 166 + .collect(); 167 + 168 + RpcMessage::PullMetadataResult(rpc::PullMetadataResult { 169 + codes: Cow::Owned(statuses), 170 + funcs: Cow::Owned(found), 171 + }) 172 + .async_write(&mut stream) 173 + .await?; 174 + }, 175 + RpcMessage::PushMetadata(mds) => { 176 + // parse the function's metadata 177 + let start = Instant::now(); 178 + let scores: Vec<u32> = mds.funcs.iter().map(md::get_score).collect(); 179 + 180 + let status = 181 + match db.push_funcs(&session.hello_msg, session.creds_id, &mds, &scores).await { 182 + Ok(v) => v.into_iter().map(u32::from).collect::<Vec<u32>>(), 183 + Err(err) => { 184 + log::error!("push failed, db: {}", err); 185 + rpc::RpcMessage::Fail(rpc::RpcFail { 186 + result: 0, 187 + error: &format!("{server_name}: db error; please try again later.\n"), 188 + }) 189 + .async_write(&mut stream) 190 + .await?; 191 + return Ok(()); 192 + }, 193 + }; 194 + state.metrics.pushes.inc_by(status.len() as _); 195 + let new_funcs = 196 + status.iter().fold(0u64, |counter, &v| if v > 0 { counter + 1 } else { counter }); 197 + state.metrics.new_funcs.inc_by(new_funcs); 198 + debug!( 199 + "push {} funcs ended after {:?} ({new_funcs} new)", 200 + status.len(), 201 + start.elapsed() 202 + ); 203 + 204 + RpcMessage::PushMetadataResult(rpc::PushMetadataResult { status: Cow::Owned(status) }) 205 + .async_write(&mut stream) 206 + .await?; 207 + }, 208 + RpcMessage::DelHistory(req) => { 209 + let is_delete_allowed = state.config.lumina.allow_deletes.unwrap_or(false); 210 + if !is_delete_allowed { 211 + RpcMessage::Fail(rpc::RpcFail { 212 + result: 2, 213 + error: &format!("{server_name}: Delete command is disabled on this server."), 214 + }) 215 + .async_write(&mut stream) 216 + .await?; 217 + } else { 218 + if let Err(err) = db.delete_metadata(&req).await { 219 + error!("delete failed. db: {err}"); 220 + RpcMessage::Fail(rpc::RpcFail { 221 + result: 3, 222 + error: &format!("{server_name}: db error, please try again later."), 223 + }) 224 + .async_write(&mut stream) 225 + .await?; 226 + return Ok(()); 227 + } 228 + RpcMessage::DelHistoryResult(rpc::DelHistoryResult { 229 + ndeleted: req.calcrel_hashes.len() as u32, 230 + }) 231 + .async_write(&mut stream) 232 + .await?; 233 + } 234 + }, 235 + RpcMessage::GetFuncHistories(req) => { 236 + let limit = state.config.lumina.get_history_limit.unwrap_or(0); 237 + 238 + if limit == 0 { 239 + RpcMessage::Fail(rpc::RpcFail { 240 + result: 4, 241 + error: &format!( 242 + "{server_name}: function histories are disabled on this server." 243 + ), 244 + }) 245 + .async_write(&mut stream) 246 + .await?; 247 + return Ok(()); 248 + } 249 + 250 + let mut statuses = vec![]; 251 + let mut res = vec![]; 252 + for chksum in req.funcs.iter().map(|v| v.data) { 253 + let history = match db.get_func_histories(chksum, limit).await { 254 + Ok(v) => v, 255 + Err(err) => { 256 + error!("failed to get function histories: {err:?}"); 257 + RpcMessage::Fail(rpc::RpcFail { 258 + result: 3, 259 + error: &format!("{server_name}: db error, please try again later."), 260 + }) 261 + .async_write(&mut stream) 262 + .await?; 263 + return Ok(()); 264 + }, 265 + }; 266 + let status = !history.is_empty() as u32; 267 + statuses.push(status); 268 + if history.is_empty() { 269 + continue; 270 + } 271 + let log = history 272 + .into_iter() 273 + .map(|(updated, name, metadata)| rpc::FunctionHistory { 274 + unk0: 0, 275 + unk1: 0, 276 + name: Cow::Owned(name), 277 + metadata: Cow::Owned(metadata), 278 + timestamp: updated.unix_timestamp() as u64, 279 + author_idx: 0, 280 + idb_path_idx: 0, 281 + }) 282 + .collect::<Vec<_>>(); 283 + res.push(rpc::FunctionHistories { log: Cow::Owned(log) }); 284 + } 285 + 286 + trace!("returning {} histories", res.len()); 287 + 288 + RpcMessage::GetFuncHistoriesResult(rpc::GetFuncHistoriesResult { 289 + status: statuses.into(), 290 + funcs: Cow::Owned(res), 291 + authors: vec![].into(), 292 + idb_paths: vec![].into(), 293 + }) 294 + .async_write(&mut stream) 295 + .await?; 296 + }, 297 + _ => { 298 + RpcMessage::Fail(rpc::RpcFail { 299 + result: 0, 300 + error: &format!("{server_name}: invalid data.\n"), 301 + }) 302 + .async_write(&mut stream) 303 + .await?; 304 + }, 305 + } 306 + Ok(()) 307 + } 308 + 309 + async fn http_reply<W: AsyncRead + AsyncWrite + Unpin>(mut stream: W) { 310 + use tokio::io::AsyncWriteExt; 311 + 312 + const HTTP_REPLY: &str = concat!( 313 + "HTTP/1.1 400 bad request\r\n", 314 + "Refresh: 2; URL=https://github.com/naim94a/lumen\r\n", 315 + "Server: lumen\r\n", 316 + "Connection: close\r\n", 317 + "Content-Type: text/html\r\n", 318 + "", 319 + "\r\n", 320 + "<pre>This is not an HTTP server. <br />Redirecting to <a href=\"https://github.com/naim94a/lumen\">lumen</a> ...</pre>\n", 321 + ); 322 + 323 + let _ = stream.write_all(HTTP_REPLY.as_bytes()).await; 324 + } 325 + 326 + async fn handle_client<S: AsyncRead + AsyncWrite + Unpin>( 327 + state: &SharedState, mut stream: S, 328 + ) -> Result<(), rpc::Error> { 329 + let server_name = &state.server_name; 330 + let rpkt = async { 331 + let hdr = rpc::read_packet_header(&mut stream).await?; 332 + if hdr.is_http() { 333 + // looks like someone is using a browser instead of IDA, what a fool. 334 + debug!("ignoring http request..."); 335 + http_reply(&mut stream).await; 336 + return Err(Error::Eof); 337 + } 338 + hdr.read(&mut stream).await 339 + }; 340 + let hello = match timeout(Duration::from_secs(15), rpkt).await { 341 + Ok(v) => v?, 342 + Err(_) => { 343 + debug!("didn't get hello in time."); 344 + return Ok(()); 345 + }, 346 + }; 347 + 348 + let (hello, creds) = match RpcMessage::deserialize(&hello) { 349 + Ok(RpcMessage::Hello(v, creds)) => { 350 + debug!("hello protocol={}, login creds: {creds:?}", v.client_version); 351 + (v, creds) 352 + }, 353 + _ => { 354 + // send error 355 + error!("got bad hello message"); 356 + 357 + let resp = rpc::RpcFail { result: 0, error: &format!("{server_name}: bad sequence.") }; 358 + let resp = rpc::RpcMessage::Fail(resp); 359 + resp.async_write(&mut stream).await?; 360 + 361 + return Ok(()); 362 + }, 363 + }; 364 + state 365 + .metrics 366 + .lumina_version 367 + .get_or_create(&LuminaVersion { protocol_version: hello.client_version }) 368 + .inc(); 369 + 370 + let creds = creds.unwrap_or(Creds { username: "guest", password: "guest" }); 371 + 372 + let user = if creds.username != "guest" { 373 + match state.db.get_user_by_username(creds.username).await { 374 + Ok((user_id, db_creds)) if db_creds.verify_password(creds.password) => { 375 + let _ = state.db.update_last_active(user_id).await; 376 + info!("{} logged in successfully.", db_creds.username); 377 + (user_id, db_creds) 378 + }, 379 + Ok(_) => { 380 + rpc::RpcMessage::Fail(rpc::RpcFail { 381 + result: 1, 382 + error: &format!("{server_name}: invalid username or password."), 383 + }) 384 + .async_write(&mut stream) 385 + .await?; 386 + return Ok(()); 387 + }, 388 + Err(err) => { 389 + error!("error while fetching user information: {err}"); 390 + rpc::RpcMessage::Fail(rpc::RpcFail { 391 + result: 1, 392 + error: &format!("{server_name}: internal error, please try again later."), 393 + }) 394 + .async_write(&mut stream) 395 + .await?; 396 + return Ok(()); 397 + }, 398 + } 399 + } else { 400 + ( 401 + -1, 402 + db::schema::Creds { 403 + username: creds.username.into(), 404 + is_enabled: state.config.users.allow_guests, 405 + ..Default::default() 406 + }, 407 + ) 408 + }; 409 + 410 + if !user.1.is_enabled { 411 + info!("attempt to login to disabled account [{}].", user.1.username); 412 + rpc::RpcMessage::Fail(rpc::RpcFail { 413 + result: 1, 414 + error: &format!("{server_name}: account disabled."), 415 + }) 416 + .async_write(&mut stream) 417 + .await?; 418 + return Ok(()); 419 + } 420 + 421 + let resp = match hello.client_version { 422 + 0..=4 => rpc::RpcMessage::Ok(()), 423 + 424 + // starting IDA 8.3 425 + 5.. => { 426 + let mut features = 0; 427 + 428 + if user.1.is_admin { 429 + features |= 0x01; 430 + } 431 + 432 + if user.0 != -1 && state.config.lumina.allow_deletes.unwrap_or(false) { 433 + features |= 0x02; 434 + } 435 + 436 + let last_active = user.1.last_active.map_or(0, |v| v.unix_timestamp() as u64); 437 + 438 + rpc::RpcMessage::HelloResult(HelloResult { 439 + username: Cow::Borrowed(&user.1.username), 440 + last_active, 441 + features, 442 + ..Default::default() 443 + }) 444 + }, 445 + }; 446 + resp.async_write(&mut stream).await?; 447 + 448 + let creds_id = if user.0 == -1 { None } else { Some(user.0) }; 449 + let mut session = Session { 450 + state, 451 + hello_msg: hello, 452 + _creds: user.1, 453 + creds_id, 454 + last_cred_check: Instant::now(), 455 + }; 456 + 457 + loop { 458 + handle_transaction(&mut session, &mut stream).await?; 459 + } 460 + } 461 + 462 + async fn handle_connection<S: AsyncRead + AsyncWrite + Unpin>(state: &SharedState, s: S) { 463 + if let Err(err) = handle_client(state, s).await { 464 + if discriminant(&err) != discriminant(&Error::Eof) { 465 + warn!("err: {}", err); 466 + } 467 + } 468 + } 469 + 470 + async fn serve( 471 + listener: TcpListener, accpt: Option<tokio_native_tls::TlsAcceptor>, state: SharedState, 472 + mut shutdown_signal: tokio::sync::oneshot::Receiver<()>, 473 + ) { 474 + let accpt = accpt.map(Arc::new); 475 + 476 + let (async_drop, worker) = AsyncDropper::new(); 477 + tokio::task::spawn(worker); 478 + 479 + let connections = Arc::new(tokio::sync::Mutex::new(HashMap::< 480 + std::net::SocketAddr, 481 + tokio::task::JoinHandle<()>, 482 + >::new())); 483 + 484 + loop { 485 + let (client, addr) = tokio::select! { 486 + _ = &mut shutdown_signal => { 487 + drop(state); 488 + info!("shutting down..."); 489 + let m = connections.lock().await; 490 + m.iter().for_each(|(k, v)| { 491 + debug!("aborting task for {k}..."); 492 + v.abort(); 493 + }); 494 + return; 495 + }, 496 + res = listener.accept() => match res { 497 + Ok(v) => v, 498 + Err(err) => { 499 + warn!("failed to accept(): {}", err); 500 + continue; 501 + } 502 + }, 503 + }; 504 + 505 + let start = Instant::now(); 506 + 507 + let state = state.clone(); 508 + let accpt = accpt.clone(); 509 + 510 + let conns2 = connections.clone(); 511 + let counter = state.metrics.active_connections.clone(); 512 + let guard = async_drop.defer(async move { 513 + let count = counter.dec() - 1; 514 + debug!( 515 + "connection with {:?} ended after {:?}; {} active connections", 516 + addr, 517 + start.elapsed(), 518 + count 519 + ); 520 + 521 + let mut guard = conns2.lock().await; 522 + if guard.remove(&addr).is_none() { 523 + error!("Couldn't remove connection from set {addr}"); 524 + } 525 + }); 526 + 527 + let counter = state.metrics.active_connections.clone(); 528 + let handle = tokio::spawn(async move { 529 + let _guard = guard; 530 + let count = { counter.inc() + 1 }; 531 + let protocol = if accpt.is_some() { " [TLS]" } else { "" }; 532 + debug!("Connection from {:?}{}: {} active connections", &addr, protocol, count); 533 + match accpt { 534 + Some(accpt) => { 535 + match timeout(Duration::from_secs(10), accpt.accept(client)).await { 536 + Ok(r) => match r { 537 + Ok(s) => { 538 + handle_connection(&state, s).await; 539 + }, 540 + Err(err) => debug!("tls accept ({}): {}", &addr, err), 541 + }, 542 + Err(_) => { 543 + debug!("client {} didn't complete ssl handshake in time.", &addr); 544 + }, 545 + }; 546 + }, 547 + None => handle_connection(&state, client).await, 548 + } 549 + }); 550 + 551 + let mut guard = connections.lock().await; 552 + guard.insert(addr, handle); 553 + } 554 + } 555 + 556 + pub(crate) async fn do_lumen(config: Arc<Config>) { 557 + info!("starting private lumen server..."); 558 + 559 + let db = match Database::open(&config.database).await { 560 + Ok(v) => v, 561 + Err(err) => { 562 + error!("failed to open database: {}", err); 563 + exit(1); 564 + }, 565 + }; 566 + 567 + let server_name = config.lumina.server_name.clone().unwrap_or_else(|| String::from("lumen")); 568 + 569 + let state = Arc::new(SharedState_ { 570 + db, 571 + config, 572 + server_name, 573 + metrics: common::metrics::Metrics::default(), 574 + }); 575 + 576 + let tls_acceptor; 577 + 578 + if state.config.lumina.use_tls.unwrap_or_default() { 579 + let cert_path = 580 + &state.config.lumina.tls.as_ref().expect("tls section is missing").server_cert; 581 + let mut crt = match std::fs::read(cert_path) { 582 + Ok(v) => v, 583 + Err(err) => { 584 + error!("failed to read certificate file: {}", err); 585 + exit(1); 586 + }, 587 + }; 588 + let pkcs_passwd = std::env::var("PKCSPASSWD").unwrap_or_default(); 589 + let id = match Identity::from_pkcs12(&crt, &pkcs_passwd) { 590 + Ok(v) => v, 591 + Err(err) => { 592 + error!("failed to parse tls certificate: {}", err); 593 + exit(1); 594 + }, 595 + }; 596 + let _ = pkcs_passwd; 597 + crt.iter_mut().for_each(|v| *v = 0); 598 + let _ = crt; 599 + let mut accpt = native_tls::TlsAcceptor::builder(id); 600 + accpt.min_protocol_version(Some(native_tls::Protocol::Sslv3)); 601 + let accpt = match accpt.build() { 602 + Ok(v) => v, 603 + Err(err) => { 604 + error!("failed to build tls acceptor: {}", err); 605 + exit(1); 606 + }, 607 + }; 608 + let accpt = tokio_native_tls::TlsAcceptor::from(accpt); 609 + tls_acceptor = Some(accpt); 610 + } else { 611 + tls_acceptor = None; 612 + } 613 + 614 + let web_handle = if let Some(ref webcfg) = state.config.api_server { 615 + let bind_addr = webcfg.bind_addr; 616 + let state = state.clone(); 617 + info!("starting http api server on {:?}", &bind_addr); 618 + Some(tokio::spawn(async move { 619 + web::start_webserver(bind_addr, state).await; 620 + })) 621 + } else { 622 + None 623 + }; 624 + 625 + let (exit_signal_tx, exit_signal_rx) = tokio::sync::oneshot::channel::<()>(); 626 + 627 + let async_server = async move { 628 + let server = match TcpListener::bind(state.config.lumina.bind_addr).await { 629 + Ok(v) => v, 630 + Err(err) => { 631 + error!("failed to bind server port: {}", err); 632 + exit(1); 633 + }, 634 + }; 635 + 636 + info!("listening on {:?} secure={}", server.local_addr().unwrap(), tls_acceptor.is_some()); 637 + 638 + serve(server, tls_acceptor, state, exit_signal_rx).await; 639 + }; 640 + 641 + let server_handle = tokio::task::spawn(async_server); 642 + tokio::signal::ctrl_c().await.unwrap(); 643 + debug!("CTRL-C; exiting..."); 644 + if let Some(handle) = web_handle { 645 + handle.abort(); 646 + } 647 + exit_signal_tx.send(()).unwrap(); 648 + server_handle.await.unwrap(); 649 + 650 + info!("Goodbye."); 651 + }
+43
lumen/src/users.rs
···
··· 1 + use std::process::exit; 2 + 3 + use common::{config::Config, db::Database}; 4 + 5 + pub struct UserMgmt { 6 + db: Database, 7 + pbkd2_iters: u32, 8 + } 9 + 10 + impl UserMgmt { 11 + pub async fn new(cfg: &Config) -> Self { 12 + let db = match Database::open(&cfg.database).await { 13 + Ok(v) => v, 14 + Err(err) => { 15 + eprintln!("failed to open database: {}", err); 16 + exit(1); 17 + }, 18 + }; 19 + 20 + Self { db, pbkd2_iters: cfg.users.pbkdf2_iterations.get() } 21 + } 22 + 23 + pub async fn list_users(&self) { 24 + let users = self.db.get_users().await.expect("failed to retreive users from database"); 25 + println!("{users:?}"); 26 + } 27 + 28 + pub async fn set_password(&self, username: &str, password: &str) { 29 + self.db 30 + .set_password(username, password.to_owned(), self.pbkd2_iters) 31 + .await 32 + .expect("failed to set user's password") 33 + } 34 + 35 + pub async fn add_user(&self, username: &str, email: &str, is_admin: bool) { 36 + let id = self.db.add_user(username, email, is_admin).await.expect("failed to add user"); 37 + println!("{username}'s id is {id}.") 38 + } 39 + 40 + pub async fn delete_user(&self, username: &str) { 41 + self.db.delete_user(username).await.expect("failed to delete user"); 42 + } 43 + }
+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);
···