+5
.github/ISSUE_TEMPLATE/config.yml
+5
.github/ISSUE_TEMPLATE/config.yml
+40
.github/workflows/docker.yml
+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
+2
.github/workflows/rust.yml
+59
CHANGELOG.md
+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
+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
Cargo.toml
+21
-12
Dockerfile
+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
+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
+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
+8
common/diesel.toml
common/migrations/.keep
common/migrations/.keep
This is a binary file and will not be displayed.
+6
common/migrations/00000000000000_diesel_initial_setup/down.sql
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+10
-8
common/src/web.rs
+13
-2
config-example.toml
+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
+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
-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
+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
+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
+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
+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
+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
+5
rustfmt.toml
-39
schema.sql
-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);
···